链表入门,学习一段时间的体会

结构体 链表 快速入门

一.结构体快速入门

之前学到结构体章节的时候糊里糊涂,课后练习也很少,只会复制粘贴,很多都没去思考。
现在又重新学到找个章节还是想对其理解加深一点。

一.定义结构体的几种方式
1.
struct book
{
	char a;	
	char b[];
};
使用时 : struct book a;定义了一个结构体变量a 
2.
typedef struct book book;
struct book
{
	char a;
	char b[10];
};
book 就指代结构体类型了, book a;定义了一个结构体变量a
3.
typedef struct book
{
	char a;
	char b[10]}book;
结合12,两句简化成一句,用法同2
4.
struct book
{
	char a;
	char b[10]}book[10];
typedef struct book book;
在定义一个结构体的同时,创建了一个结构体变量book[10];
最后一句typedef是为了以后再次创建结构体变量时省事用,
且最大原因其实是 既定义结构体同时又创建一个结构体变量不能用typedef

二.结构体变量如何访问结构体内容
struct book
{
	char a;
	char b[10]}book[10];
结构体指针是 *b->a;或者  (*b).a
为什么不用 *b.a 访问呢,因为C语言 *. 都是运算符,. 的优先级高
先算右边咋算,b是一个指针,只有在b是一个值的时候才能用 . 先取地址的值就能 如上
这里简单介绍  book[0].a;(加小数点 .

二.链表如此简单?

1.啥是链表,结构体里放指针就是链表。
看到这的应该都看了一,下面我就用自己常用的定义结构体的方式写。

typedef struct Node		//连锁店
{
	int data;			//店铺       
	struct Node * next;//店铺地址
}Node;					//店铺名字
联想记忆法嘛,像我,从连锁店挑选店铺,搜索店铺地址,去吃了下次就记住名字了嘛。
像那个香飘飘,一年卖下来的奶茶连起来绕地球好几圈儿。

链表 是动态存储分配 运行时分配空间的 当不能确定容量时使用是极好的 但这是不连续的 零散分布
单链表的存储特点:1.逻辑次序和物理次序不一定相同 2.元素之间的逻辑关系用指针表示。
笔者个人认为 在敲代码之前 先画一个简易的示意图来说明比较好
单链表的结点结构: 数据域+指针域;  指针域的指针就是指向下一个同类型结点的地址
tepedef struct node
{
	Datatype data;
	struct node *next;
}Node,*Link;			//这里表示 Node类型 *Link类型
当你 Link p;就是声明了一个结构体指针变量 Noed s;是结构体s  p=&s;p就指向了s;
在使用时先申请空间 p=(Link)malloc(sizeof(Node)); 等价于 p=(struct noed *)malloc(sizeof(Node));
当初始化结束后,想访问数据元素, (*p).data || p->data  不能 *p.data 上面已经叙述了,这里不再赘述 指针域 p->next;
为了简化单链表的代码操作:使用 头指针和尾标志 头:指向第一个结点的地址, 尾:终端结点的指针域为空

先引入一个头节点, 数据域无意义,只使用指针域 当 指针域为NULL,称空表 else 非空表
(注:假定链表已创立)
(1)单链表的遍历操作 计数操作
void displayNode(Link head) //传参头指针
{							//逻辑上没错
	p=head->next;		
	count = 0//这里目的是告诉你得先初始化
	while(p!=NULL)
	{
		printf("%d",p->data);
		p=p->next;
		count++//用来计单链表个数
	}
	return count;			//因为函数内的变量只能在块内使用,所以返回值才能使用
}
这个时候可以求单链表的个数
(2)单链表得查找操作
int queryNode(Link head,DataType x) //传参 头结点,数据域变量 x
{
	p=head->next;
	while(p!=NULL) 			//P不为空时执行如下操作
	{
		if(p->data==x)		//数据与匹配成功
		{
			print(data);	//输出
			return true;	//提前返回 true
		}
		p=p->next			//没找到继续下一个
	}
	return false;			//循环结束,还没找到 退出 (设置成布尔类型也行)
}3)单链表得插入操作
已知两个结点,我想在其中插入,如何实现
声明一个新的结点 互换数据域和指针域即可,所以画图很重要 这里笔者奉劝各位 自己画一下就懂了
(注意一点,新结点仍然要申请空间;注意二点,分析边界情况———表头,表尾)
算法描述 (由于单链表带头节点,表头,表中,表尾三种情况的操作语句一致)
noed=(Link)malloc(sizeof(Node));	
node->data = x;						
node->next = p->next;	
p->next = node;			
1.工作指针P初始化	
2.查找第i-1个结点并使工作指针p指向该节点				
3.若查找不成功,则返回false,
否则生成一个元素值为x的新节点s;
将新节点s插入到结点p之后;
返回truevoid insertNode(Link head,int i,DataType x) //参数 i 表示插入位置 
{
	p = head;
	count = 0;
	while(p!=NULL&&count<i-1)				// 需要用count来判断访问到第几个结点
	{										//p不为空表示在链表内插入 
		p=p->next;
		count++;
	}
	if(P==NULL)
		return false;
	else
	{
		node = (Link)malloc(sizeof(Node));
		node->data = x;
		node->next = p->next;
		p->next = node;
		return true;
	}
}	
2.单链表的实现
(注意,事先定义数组来存数据)
(1)创建一个单链表——头插法;理论学完了,搞点实际的。
头插法就是将插入结点插在头节点的后面,这个存入的顺序和数组是相反的,编译打印的时候注意。
Link newList(DataType a[],int n)
{	//创建头结点
	head=(Link)malloc(sizeof(Node));
	head->next=NULL;				//切勿忘记初始化时第一个next指向NULL
	//创建后续结点
	for(i=0;i<n;i++)
	{
		node=(Link)malloc(sizeof(Node));
		node->data=a[i];
		node->next=head->next;
		head->next=node;
	}
	return head;
}2)创建一个单链表——尾插法
将待插入结点插在终端结点的后面
初始化head结点,新结点依次插在后面就行,切勿忘记初始化时第一个next指向NULL
Link newList(DataType a[],int n)
{
	head = (Link)malloc(sizeof(Node)); //生成头结点
	head->next = NULL;				   
	rear = head;					   //尾指针初始化
	for(i=0;i<n;i++)
	{
		node=(Link)malloc(sizeof(Node));
		node->data=a[i];
		//node->next=NULL;	//注意这条语句,和尾部NULL语句 希望你每次创建结点时就给所有结点初始化
		rear->next=node;
		rear=node;
	}
	rear->next=NULL;					//这条语句很容易忘记		
	return head;		//返回头指针
}3)单链表结点的删除
①.先找到要删除的位置 
简述 q->next = p->next; free(p);  简化图像 q p next (删除 p)
②.如何保证 p ,q 指针一前一后
同时移动指针一次:q=p;p=p->next;
(在查找过程中,如果发现p所指向的结点data值不是要找的x,则p,q同时后移;一旦找到,则执行删除操作。)
(在查找过程中,如果若一直没有找到data域为x的结点,q没事,p为空,此时退出循环,返回false)
③.删除时要分析边界情况——要删的表为空表时
比如 head=NULL,或者只有头结点
(如果发现待删除的表是空表,则提前返回false)
算法描述:
1.判断是否是空表,如果是空表返回false2.工作指针p,q初始化;
3.若p指针不为空,则继续下列循环:
3.1如果找到data域等于x的结点,则:
	3.1.1摘链,将结点p的从链表上摘下;
	3.1.2释放被删结点;
	3.1.3提前返回true,代表删除成功
	否则
	3.2.1 q 移动到 p 所在的位置
	3.2.2 p 移动到下一个结点
④.循环结束,说明没找到和x相等的结点,则返回false;
(笔者感受:这就是学习的魅力,前人栽树后人乘凉,感谢大佬.自己写的时候考虑从来没有这么完善)

bool deleteNode(Link head,DataType x)
{
	if(head==NULL||head->next==NULL)		//若链表没有数据,空表
		return false;
	p=head->next;							//初始化,p,q两个指针一前一后
	q=head;
	while(p!=NULL)
	{
		if(p->data==x)						//找到x结点,删除,并提前返回
		{
			q->next=p->next;
			free(p);
			return true;
		}
		else
		{
			q=p;
			p=p->next;
		}
	}
	return false;		//循环结束还没找到,退出
}4)单链表的释放
将单链表中所有结点的存储空间释放(好处多多)(头结点不保留)
算法描述
q=head;
head=head->next;
free(q);
问(代码循环退出的条件是什么呢?)
(笔者认为是head->next=NULL3.循环链表
首尾相接,循环链表,单循环很容易理解,尾连首即可。链表操作同上。
为了避免死循环,判断条件由不为空变成不为head即可。

猜你喜欢

转载自blog.csdn.net/GameStrategist/article/details/106031592