【程序设计基础 学习笔记】单向链表(TBC)

好耶,开始学链表了。尽管单向链表内容比较简单,对C语言一无所知的蒟蒻还是决定写下来,以帮自己捋清逻辑并供复习使用。
因为是初学者,代码码风很丑,希望大家多多包涵(抱拳),欢迎各路大神的指点!
感谢来自PKU信科的dalao—Lucario的拨冗指点。
这张图给菜鸡博主整破防了

简介
链表,顾名思义,是一种链状的结构。类比以前用来建图的前向星算法就比较好理解了。而实际上单向链表的结构比一般的图更加简单,它既不存在双向边和环,也不存在分支,只有一条长长的主链,即:除首尾节点外,每一个节点的出度和入度都是1 (虽然这个性质似乎没啥实用价值23333)
我们打个比方。假如现在有一间很大的教室,每一列有很多很多的位置。这让80岁高龄的lgg老师非常头疼,他无法记清楚每个同学的位置,而调皮的同学们显然是不会自己乖乖坐在自己座位上的(事实上同学们是无法自己坐到座位上的)。为了能够成功地给(和)同学们上(扯)课(淡),lgg老师想了一个好办法,他选了一个科代表(神仙glf)并让他坐在第一排,这样lgg老师就知道了坐在第一排的是谁,然后让每个人都记住自己的后桌是谁,而坐在最后一排的同学(蒟蒻lry)则记住自己后面是墙就可以了。现在每次收作业的时候就从科代表开始,每个人把作业交给lgg老师后就扯一嗓子,喊下一个同学上来交。这种方法lgg老师很满意,因为他只需要记住第一排的是glf神仙,其他的事情他就不用管了。

一.链表的优点
根据链表的图示我们可以发现,链表与一维数组非常相似。在检索功能上,一维数组的遍历显然更好写一些;但是在一个动态的过程中,链表不仅可以实现内存的动态分配,在修改维护上也更加优秀。举个例子,当前链长100,假如要在2号节点和3号节点之间插入一个新的节点,我们需要将数组开为101,再把三号节点及其后面所有节点全部向后平移一位。而链表却只需要简单地删边和加边。在上个例子中,可以认为从三号同学开始,后面每个同学的相对位置是没有变的,不需要修改。链表这种结构就是充分利用了这一点,优化了效率。

二.定义节点的数据类型

typedef struct node{
	int val;//每个节点的权值
	struct node *next; //下一个节点的地址
}linkedlist;

这个好像没啥可说的
三.链表的创建Create

node *Createlist(int n){
	node *head,*x,*end;
	/*
	蒟蒻的写法中并没有为head单独malloc,而是只用一个指针储存头结点的地址
	  现在主流一点的写法是专门malloc一个虚点来作为head,但它的val并没有被赋值
	  这样的写法在insert等函数中比较方便,不需特判插入点是否会作为新的头节点
	*/
	head=(node *)malloc(sizeof(node));
	scanf("%d",&head->val);
	end=head;
	for(int i=1;i<=n-1;i++){
		x=(node *)malloc(sizeof(node));
		scanf("%d",&x->val);
		end->next=x;
		end=x;
	}
	end->next=NULL;//注意别忘了这一步,它是我们遍历的边界,也是后面递归的边界
	return head;
} 

由于n是传递的参数,所以其内存分配是动态的。这个Createlist 函数是node*类型的函数,它返回的head就是链表中第一个节点的地址。所以主函数里我们应该写为:

	node *head=Createlist(n);

四.遍历
之前已经提过,遍历的边界应该是指针指向NULL的时候。

	node *pre=head;
	while(pre!=NULL){
		printf("%d ",pre->val);
		pre=pre->next;
	}

五.插入新节点 insert
插入一个新节点作为新的第index号节点

node* insertpoint(node *list,int index){
	if(index==1){
		node *newpoint=(node *)malloc(sizeof(node));
		newpoint->next=list;newpoint->val=114514;
		list=newpoint;
		return list;
	}
	else{
		node *newpoint=(node *)malloc(sizeof(node));
		newpoint->val=114514;
		node *t,*last=list;
		for(int i=1;i<=index-2;i++){
			last=last->next;
			if(last==NULL){
				printf("Error\n");
				return list;
			}
		}
		t=last->next;
		newpoint->next=t;
		last->next=newpoint;
	} 
	return list;
} 

main函数中:

	int index=read();
	list=insertpoint(list,index);

六.删除节点 deletepoint
删除链表中第index号节点

node *deletepoint(node *list,int index){
	if(index==1){
		node *newlist=list->next;
		free(list);
		return newlist;
	}
	else{
		node *t,*last=list;
		for(int i=1;i<=index-2;i++){
			last=last->next;
			if(last==NULL){
				printf("Error\n");
				return list;
			}
		}
		t=last->next;
		last->next=t->next;
		free(t);
		return list; 
	}

main函数中:

	int index=read();
	list=deletepoint(list,index);

七.链表的反序 reverse
反序是链表中最重要的操作之一。
递归写法
递归思想是当我处理某节点的时候,我先保证后续节点形成的子链是我已经反序处理好的。对整个链表的反序就可以拆分为两个子问题:处理当前节点,以及处理后面的子链。这个时候应该return 什么东西呢?我们回到上一个例子。现在lgg老师怕坐在最后一排的同学上课摸鱼摸得太多,决定让坐在后面的同学坐到前面来。那么反序过后,现在lgg老师需要知道的是什么呢?他需要知道以前坐最后一排的是哪位同学(lry,危)。所以我们从最后一排开始,不仅要反过来记住自己前面的是哪个同学,还得从后往前传话,把摸鱼者是lry的消息告诉lgg老师。

node *reverse(node *head){
	if(head==NULL||head->next==NULL){
		return head;
	} 
	node* newhead=reverse(head->next);
	head->next->next=head;
	head->next=NULL;
	return newhead;
} 

传话给了glf神仙处后,我们还需要让lgg老师重新记忆一下(因为lgg老师不是一个节点,递归返回只会返回到glf神仙)。所以主函数应该这样写:

	head=reverse(head);
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值