C的回归基础学习——数据结构(2)链表(临时)

C的回归基础学习——数据结构(2)链表(临时)

前言

链表我是没有完整学过的,但是在学习过程中发现自己已经或多或少的见过或者用过这玩意儿了,这也说明链表是一个比较基础且常用的数据结构。这篇的重点会在基础上偏重一点

链表的实现

链表实际上是一个用处较多而且模板较为单一(至少以我现在的水平是这么认为的)所以直接上代码,以注释作为主要讲解。

#include <cstdio>
#include <cstdlib>
typedef struct student{
	int score; 
	struct student *next;
}linkList;//定义一个链表

linkList* initList() { //初始化一个链表,实际上就是定义一个头结点 
	linkList* headNode = (linkList*)malloc(sizeof(linkList)); 
	if (headNode == NULL) {
		
		printf("空间缓存不足\n");
		return headNode;
	}
	headNode->score = 0;
	headNode->next = NULL;
	return headNode;
} 

/*void createList(linkList* headNode ,int n) {//建立一个链表,其中头结点中score有意义 即有n个节点 
	linkList* endNode ;                     // 其实这样会有点小问题,就是在1处添加或删除某一个元素时,需要特殊操作 
	endNode = headNode;
	for(int i = 0;i < n ;i++)
	{
		scanf("%d",&endNode->score);
		if (i < n - 1)//防止出现废结点,即最后一个结点 
		{
			endNode->next = (linkList*)malloc(sizeof(linkList));
			endNode = endNode->next; 
		}
	}
	endNode->next = NULL; 
} */ 

void createList(linkList* headNode ,int n) {//建立一个链表,其中头结点中score值无意义 即有n+1个结点 
	linkList *endNode,*nowNode ;
	endNode = headNode;
	for(int i = 0;i < n ;i++)
	{
		nowNode = (linkList*)malloc(sizeof(linkList));
		scanf("%d",&nowNode->score);
		endNode->next = nowNode;
		endNode = nowNode;
	}
	endNode->next = NULL; 
} 

void outList(linkList* headNode)//输出一个链表
{
	linkList* nowNode=headNode->next;
	while(nowNode) {
		printf("%d ",nowNode->score);
		nowNode = nowNode->next;
	}
	printf("\n");
}
void modifyList(linkList* headNode,int pos,int num) {//将第pos个结点的值修改为num
	linkList* nowNode = headNode;
	for(int i = 0 ;i < pos&&nowNode != NULL;i++) {
		nowNode = nowNode->next;
	}
	if (nowNode != NULL) nowNode->score = num; 
	else printf("该结点不存在\n");
} 

bool insertList(linkList* headNode,int pos,int inData) {//在第pos个位置插入一个结点,内值为inData  
	linkList* nowNode = headNode;
	linkList* tmp = (linkList*)malloc(sizeof(linkList));
	tmp->score = inData;
	if(tmp == NULL)
	{
		printf("空间缓存不足\n");
		return false;
	}
	for(int i = 0;i < pos-1&&nowNode != NULL;i++) {
		nowNode = nowNode->next;
	}
	
	if(nowNode == NULL)
	{
		printf("结点输入过大,无法加入\n");
		return false;
	 } 
	tmp->next = nowNode->next;
	nowNode->next = tmp;
	
	return true;
}

int deleteList(linkList* headNode,int pos) {//将第pos个位置的结点删除,并返回删除结点的值 
	linkList* nowNode = headNode;
	int dataToDelete;
	for(int i = 0;i < pos-1&&nowNode != NULL;i++) {
		nowNode = nowNode->next;
	} 
	if(nowNode == NULL)
	{
		printf("结点不存在\n");
		return false;
	 } 
	linkList *nodeToDelete = nowNode->next;
	dataToDelete = nodeToDelete->score;
	nowNode->next = nodeToDelete->next;
	free(nodeToDelete);
	return dataToDelete;
}//这里为什么不用 nowNode ->next = nowNode ->next->next
//因为这样free不了内存会浪费 
int main()
{
	freopen("a.txt","r",stdin);
	int n;
	scanf("%d",&n);
	linkList *test;
	test = initList();
	
	createList(test,n);
	outList(test);
	
	insertList(test,1,0);
	outList(test);
	
	deleteList(test,9);
	outList(test);
	
	deleteList(test,4);
	outList(test);
	
	modifyList(test,1,90);
	outList(test);
	
	return 0;
	/*结果:
	1 2 3 4 5 6 
	0 1 2 3 4 5 6 
	结点不存在
	0 1 2 3 4 5 6 
	0 1 2 4 5 6 
	90 1 2 4 5 6 
 */
}

其实我记得两个数组nxt[],value[]好像也可以实现简易链表??

来点实战

1.合并两个有序链表(简单)

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

示例:
输入:1->2->4       1->3->4
输出:1->1->2->3->4->4

分析:

  • 原谅我不会读题!我的眼睛直接把有序这两个字给吃了。。。想了半天说难道还要链表排序。打了半天没打出来,又跑去看题解,结果简单的递归就出来了。。。
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        if(l1 == NULL) {
        	return l2;
		}
		else if(l2 == NULL) {
			return l1;
		}
		else if(l1->val < l2->val) {
			l1->next = mergeTwoLists(l1->next,l2);
			return l1;
		}
		else {
			l2->next = mergeTwoLists(l1,l2->next);
			return l2;
		}
    }//leetcode21
  • 打完后发现这玩意就是当初学归并排序的归并步骤,难道说还可以用链表来排序?!!思索一番感觉可行,但寻找中点会有点困难,此时隐约想起自己在学链表时看到的一篇关于找链表某位置的博客,翻了一翻,发现slow-fast法!!ok,没问题了,开打!上代码!
#include <cstdio>
#include <cstdlib>
struct ListNode {
    int val;
    ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};
ListNode* init() {
	ListNode* headNode = (ListNode*)malloc(sizeof(ListNode));
	if (headNode == NULL) {
		
		printf("空间缓存不足\n");
		return headNode;
	}
	headNode->val = 0;
	headNode->next = NULL;
	return headNode;
}

void create(ListNode* headNode) {
	ListNode *endNode;
	endNode = headNode;
	int n;
	scanf("%d",&n);
	for(int i = 0;i < n;i++)
	{
		scanf("%d",&endNode->val);
		if(i < n - 1) {
			endNode->next = (ListNode*)malloc(sizeof(ListNode));
			endNode = endNode->next; 
		}
	}
	endNode->next = NULL;
}

void out(ListNode* headNode)
{
	ListNode* nowNode=headNode;
	while(nowNode) {
		printf("%d ",nowNode->val);
		nowNode = nowNode->next;
	}
	printf("\n");
}
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
    if(l1 == NULL) {
        	return l2;
	}
	else if(l2 == NULL) {
		return l1;
	}
	else if(l1->val < l2->val) {
		l1->next = mergeTwoLists(l1->next,l2);
		return l1;
	}
	else {
		l2->next = mergeTwoLists(l1,l2->next);
			return l2;
	}
}    
ListNode* Gsort(ListNode* l1) {
	if(l1->next == NULL) return l1;//只有一个元素就直接返回!
	
	ListNode *slow,*fast;
	slow = l1;
	fast = l1->next;//此处让fast从第二个开始是为了slow=(lenth+1)/2 
	while(fast&&fast->next) {
		slow = slow->next;
		fast = fast->next->next;
	}
	
	ListNode *right,*left; 
	left = l1;
	right = slow->next;
	slow->next = NULL;
	
	left = Gsort(left);
	right = Gsort(right);
	
	ListNode *newList;
	newList = mergeTwoLists(left, right);
	return newList;
}
int main()
{
	freopen("a.txt","r",stdin);
	ListNode *L1;
	L1 = init();
	create(L1);
	L1 = Gsort(L1);
	out(L1);
	return 0;
} 
2.链表模拟加法

给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807

分析:

  • 我一开始还以为挺简单的,就是模拟加法。但是实际操作起来。。。一共改了不下于10次才过,改到崩溃。。。
  • 主要是以下几点
    1.注意进位
    2.注意两者为不同长度
    3.注意像5+5=10这种最后进一位的情况
    4.以及各种对链表的不熟练。。。
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
    ListNode *nowNode1,*nowNode2,*newList,*nowNode3;
    nowNode1 = l1;
    nowNode2 = l2;
    newList = new ListNode(0);
    nowNode3 = newList;
    int flag = 0,num1,num2;
    while(nowNode1 != NULL || nowNode2 !=NULL) { 
    	nowNode3->next = new ListNode(0);
    	nowNode3 = nowNode3->next;
    	if (nowNode1 != NULL) num1 = nowNode1->val;
    	else num1 = 0;
    	if (nowNode2 != NULL) num2 = nowNode2->val;
    	else num2 = 0;
    	nowNode3->val = num1 + num2 + flag;
		if(nowNode3->val >= 10) {
			flag = nowNode3->val / 10;
			nowNode3->val %= 10;
		} 
		else {
			flag = 0;
		}
		if (nowNode1 != NULL) nowNode1 = nowNode1->next;
    	if (nowNode2 != NULL) nowNode2 = nowNode2->next;
	}
	if (flag > 0){
		nowNode3->next = new ListNode(0);
		nowNode3 = nowNode3->next;
		nowNode3->val = flag;
		
	}
	nowNode3->next = NULL;
	return newList->next;
}
3.k个反转

给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
分析就直接打在代码里了

ListNode* reverseKGroup(ListNode* head, int k) {
    ListNode* newHead = new ListNode(0);
    newHead->next = head;
    ListNode* now = head; //反转时的当前结点 
    ListNode* pre = newHead;//反转链表之前的结点 
    ListNode* tail = head;//反转链表最后一个结点 
    int cnt;
    while(true) {
    	cnt = 0;
    	tail = pre;
    	while(tail != NULL && cnt < k) {
    		tail = tail->next;
    		cnt++;
		}
		if(tail == NULL) break;//检测是否还剩k个 ,不够就结束 
		
		while(pre->next != tail) {
			now = pre->next;
			pre->next = now->next;//先将该链表最前一个元素孤立出来
			
			now->next = tail->next;
			tail->next = now;//再把now插入到tail和tail后面的结点之间  
		} 
		pre = head;//此时的head就是反转了的字符串的最后一位(原第一位) 
		head = pre->next;//这个地方当时想了好一会找不到比较好的处理方法,就借鉴了别人的 
	}
    return newHead->next; 
}

后记

  • 现在打的代码都很冗杂,可能有一堆不需要的东西,还需要慢慢优化。
  • 链表和顺序数组给我的感觉就是,一个便于修改一个便于查询,都是各有千秋的。
  • 总而言之要背的模板喜加一?(其实简易链表也可以用,如邻接表什么的)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值