【自学】链表

主程序

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
//结构体定义 
struct stud_node {
	int num;
	char name[20];
	int score;
	struct stud_node * next;
}; 
//函数声明
struct stud_node * Create_Stu_Doc (void);
struct stud_node * InsertDoc (struct stud_node * head, struct stud_node * stud,int target);
struct stud_node * DeleteDoc (struct stud_node * head, int num);
void Print_Stu_Doc (struct stud_node * head);

int main (void){
	struct stud_node *head, *p;
	int choice, num, score,target;
	char name[20];
	int size = sizeof(struct stud_node);
	
	do{
		printf("1: Create 2: Insert 3: Delete 4: Print 0: Exit\n");
		scanf("%d",&choice);
		switch(choice){			
			case 1:
				head = Create_Stu_Doc ();
				break;
			case 2:
				if(head == NULL) {
					printf("No Data Yet! Please Create A Node First\n");
					break;
				}
				target = -1;
				printf("Insert A Student's Data After Num ");
				scanf("%d",&target);
				putchar('\n');				
				printf("Input num, name and score: \n");
				scanf("%d%s%d",&num,name,&score);
				p = (struct stud_node*)malloc(size);
				p->num = num;
				strcpy(p->name,name);
				p->score = score;
				head = InsertDoc(head,p,target);
				break;
			case 3:
				printf("Input num: \n");
				scanf("%d",&num);
				head = DeleteDoc (head,num);
				break;
			case 4:
				Print_Stu_Doc (head);
				break;
			case 0:
				break;
		}
	}while (choice != 0);
	
	return 0;
}

链表的建立

函数的输入为,返回值为strcut stud_node*

//按照输入顺序建立单向链表
struct stud_node * Create_Stu_Doc (void){
	int num,score;
	char name[20];
	int size = sizeof (struct stud_node) ;
	struct stud_node *head, *tail, *p;
	head = tail = p =NULL;
	printf("Input num, name and score: \n");
	scanf("%d%s%d",&num,name,&score);
	while (num != 0){
		p = (struct stud_node*)malloc(size);
		p->num = num;
		strcpy(p->name,name);
		p->score = score;
		p->next = NULL;
		if(head == NULL) 
			head = p;
		else 
			tail->next = p;
		tail = p;
		scanf("%d%s%d",&num,name,&score);
	}
	return head;
}

由于需要新建一个链表,所以不需要输入。在定义的时候按照结构体里面的内容,定义int num,score;以及char name[20];等内容。通过sizeof来测量出链表的空间,以便动态内存申请。对于一个链表,需要有头(head)和尾(tail)以及指向结点的指针(p)。为了安全,让这些strcut stud_node *型的指针全部为NULL。#读入相应的数据。当编号不为0的时候,申请内存空间,然后让对应赋值(name需要用字符串处理函数进行处理)。对于指针head来说,如果是NULL的话,让他等于第一次申请时p的地址;否则由于已经有了链表而且tail指向上一个结点,通过tail->next = p;将上一个节点和新建的节点连接起来。再次读入数据判断是否结束,然后返回这个链表的头指针。
注意:链表的头一旦确定,不在改动,但是链表的尾会随着节点的变动而变动,指针p的作用是用来指向动态内存分配后的那个内存空间。
在这里插入图片描述
如果要反向建立结点,就使用p->next = head; head = p;最后的结果就是头变成尾,不需要tail了(因为head其实承担了tail的作用)

链表的遍历

void Print_Stu_Doc (struct stud_node * head){
	struct stud_node * ptr = head;
	if(ptr == NULL) {
		printf("\nNo Records!\n");
		return ;
	}
	printf("\nThe Students' Records Are: \n");
	printf("Num \t Name \t Score \n");
	while( ptr != NULL ){
		printf("%d \t %s \t %d \n",ptr->num,ptr->name,ptr->score);
		ptr = ptr->next;
	}
	return ;
}

链表在遍历的时候,需要传入头的指针,没有返回值。原理就是头指针的值给ptr,如果ptr为NULL,那么就说明没有数据,直接return ;即可。如果不是NULL,那么就需要通过while循环遍历链表,每次遍历包括打印内容和让ptr指向下个结点,最终指向NULL的时候说明链表结束。
在这里插入图片描述

添加结点

struct stud_node * InsertDoc (struct stud_node * head, struct stud_node * stud,int target){
	struct stud_node *ptr = head;
	while ( target != ptr->num && ptr != NULL){
		ptr = ptr->next;
	}
	if(ptr == NULL){
		printf("Not Found\n");
		return head;
	}
	stud->next = ptr->next;
	ptr->next = stud;
	return head;
}

首先传入的参数是两个指针head和stud,以及整型target。在主函数可以知道调用这个函数之前,对head是否为NULL做了一次判断,如果是NULL的话就要先创建一个链表,再执行插入的语句。传入的head是原来链表的头,为了遍历用。stud是新创建结点的地址,target代表插入到这个编号的后面。对链表进行遍历,如果找到target,ptr指向那个编号在的结点;如果没找到,输出Not Found。找到了以后,先将新增结点和后面的结点连接,在断开原来的连接,最终实现插入一个结点的目的。
在这里插入图片描述

删除结点

struct stud_node * DeleteDoc (struct stud_node * head, int num){
	struct stud_node *ptr1, *ptr2;
	while(head !=NULL && head->num==num){
		ptr2=head;
		head=head->next;
		free(ptr2);
		ptr2=NULL;
	}
	if (head == NULL){
		printf("No Data\n");
		return NULL;
	}
	ptr1=head;
	ptr2=ptr1->next;
	while (ptr2 != NULL){
		if(ptr2->num==num){
			ptr1->next=ptr2->next;
			free(ptr2);
			ptr2=NULL;
		}else{
			ptr1 = ptr2;
			ptr2 = ptr1->next;
		}
	}
	return head;
}

函数传入了头指针和删除结点的num。如果要删除一个头结点的话,只需要将头向后移动一个结点,然后free之前的头即可。如果要删除一个非头结点,那么需要让ptr1指向ptr2所指的结点的前一个结点。当ptr2指向要删除的结点时,让ptr1->next(注意不是ptr1)指向删除结点的下个结点即ptr2->next,然后free(ptr2)即可。
在这里插入图片描述

总结

以上便是对链表的基本操作,各个函数的功能可以随着具体的问题需求改变,传入的参数和返回的参数都可以做出改变。比如插入和删除的时候可以将函数定义成void类型。但是定义成struct stud_node * 类型可以后期修改时返回插入结点的指针,进行一些操作。
程序参考何钦铭与颜晖主编的C语言程序设计第三版11.3,并进行了一定的修改。如有不对,不合理之处,还望指出。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
链表是一种常用的数据结构,由一系列节点组成,每个节点包含了数据和指向下一个节点的指针链表的优势在于可以动态地添加和删除节点,相比于数组,它的大小不需要提前确定,而且在插入或删除节点时不需要移动其他节点,因此效率更高。 链表结点类listnode的定义如下: ```cpp class listnode { public: int val; listnode* next; listnode(int x) : val(x), next(nullptr) {} }; ``` 链表类list的必要成员数据和成员函数如下: ```cpp class list { private: listnode* head; // 链表指针 public: list() : head(nullptr) {} // 默认构造函数,初始化为空链表 void append(int val); // 在链表末尾添加一个节点 void remove(int val); // 删除值为val的节点 void reverse(); // 反转链表 void print(); // 打印链表 }; ``` 链表中的数据类型为int,因此我们需要添加一个节点时,只需要传入一个int参数val,即可创建一个新节点,代码如下: ```cpp void list::append(int val) { listnode* new_node = new listnode(val); // 创建新节点 if (head == nullptr) { head = new_node; // 如果链表为空,新节点作为头节点 } else { listnode* p = head; while (p->next != nullptr) { p = p->next; // 找到链表末尾 } p->next = new_node; // 将新节点添加到末尾 } } ``` 删除节点时,需要遍历链表,找到值为val的节点,并将其从链表中删除,代码如下: ```cpp void list::remove(int val) { if (head == nullptr) { return; // 如果链表为空,直接返回 } if (head->val == val) { listnode* tmp = head; head = head->next; delete tmp; // 如果头节点就是要删除的节点,直接删除 return; } listnode* p = head; while (p->next != nullptr && p->next->val != val) { p = p->next; // 找到要删除的节点的前一个节点 } if (p->next != nullptr) { listnode* tmp = p->next; p->next = tmp->next; delete tmp; // 删除节点 } } ``` 反转链表时,需要遍历链表,将每个节点的next指针指向它的前一个节点,代码如下: ```cpp void list::reverse() { listnode* pre = nullptr; listnode* cur = head; while (cur != nullptr) { listnode* nxt = cur->next; // 保存下一个节点 cur->next = pre; // 反转指针 pre = cur; cur = nxt; } head = pre; // 头指针指向反转后的第一个节点 } ``` 打印链表时,只需要遍历链表,将每个节点的值打印出来,代码如下: ```cpp void list::print() { listnode* p = head; while (p != nullptr) { cout << p->val << " "; p = p->next; } cout << endl; } ``` 在主函数中,我们可以根据给定的整型数组构造一个链表,并将链表反向输出,代码如下: ```cpp int main() { int a[] = {1, 2, 3, 4, 5}; int n = sizeof(a) / sizeof(int); list l; for (int i = 0; i < n; i++) { l.append(a[i]); // 构造链表 } l.reverse(); // 反转链表 l.print(); // 输出链表 return 0; } ``` 输出结果为:5 4 3 2 1

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值