学习目的
看懂能敲出链表,看懂伪算法,有机会自学数据结构。
链表
顺序存储(连续存储)
顺序存储的缺点
以C语言数组插入一个元素为例,当我们需要在一个数组{1,2,3,4,5,6,7}的第1个元素后(即第2个元素)的位置插入一个’A’时。我们需要做的有,将第1个元素后的整体元素后移,形成新的数组{1,2,2,3,4,5,6,7},再将第2个元素位置的元素替换为我们所需要的元素’A’,最终形成我们的预期,一个简单的插入操作要进行那么多的步骤。
其一:所需要移动的元素很多,浪费算力。
其二:必须为数组开足够多的空间,否则有溢出风险。
链表-链式存储
定义
n个节点离散分配,彼此通过指针相连,每个节点只有一个前驱节点,一个后续节点,首节点没有前驱节点,尾节点没有后续节点。
专业术语:
首节点:第一个有效的节点。
尾节点:最后一个有效的节点。
头节点:首节点之前的节点。
头节点的数据类型和首节点数据类型相同
不存放有效数据,不存放有效节点的个数。
头节点的作用:方便对链表进行操作。
头指针:指向头节点的指针变量。
尾指针:指向尾节点的指针变量。
确定一个链表需要几个参数:
一个参数:头指针
链表节点如何开辟:
# include <stdio.h>
typrdef struct Node
{
int data;//数据域
struct Node * pNext;//指针域
}NODE, *PNODE;//NODE等价于struct Node, PNODE等价于struct Node *。
int main(void)
{
return 0;
}
同任何的结构,类型一样,链表也需要初始化操作,初始化是创建一个单链表的前置节点并向后逐步添加节点,一般来说,我们所谓的初始化单链表一般指的是申请结点的空间,同时对一个结点辅以空值(NULL)
(一定要注意是否开辟空间失败)
分类
单链表:单链表是一种链式存取的数据结构,,链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。以“结点的序列”表示的线性表称作线性链表(单链表),单链表是链式存取的结构。
双链表:每一个节点有两个指针域。
循环链表:能通过任何一个节点找到其他全部节点。
非循环链表
伪算法
非循环单链表插入节点伪算法:
p指向一个节点,q指向一个节点,r指向一个节点
1.先把要插入位置前一个节点的指针域赋给另外一个值。
再把要插入的前一个节点的指针域指向要插入的值。
最后使插入的节点的指针域指向要插入位置的下一个节点。
r = p->pNext;
p->pNext = q;
q->pNext = r;
2.
q->pNext = p->pNext;
p->pNext = q;
删除非循环单链表删除伪算法:
删除p节点后面的节点,即使p节点直接指向下下个节点
先定义一个指向p后面节点的指针r//为了释放内存,同时使链表不会断掉
r = p->pNext;
p->pNext = r->pNext;
free(r);
算法
遍历,查找,清空,销毁,求长度,排序,删除节点
链表创建:
在初始化之后,就可以着手开始创建单链表了,单链表的创建分为头插入法和尾插入法两种,两者并无本质上的不同,都是利用指针指向下一个结点元素的方式进行逐个创建,只不过使用头插入法最终得到的结果是逆序的。(下面的程序用的是尾插入法)
链表遍历:
进行遍历的思路极其简单,只需要建立一个指向链表L的结点,然后沿着链表L逐个向后搜索即可。
简单的遍历设计的函数只需要void无参即可,而当我们需要进行元素修等涉及到元素操作时,我们可以设计一个PNODE类型的函数,使其返回一个修改后的新链表。
# include <stdio.h>
# include <malloc.h>
# include <stdlib.h>
PNODE creat_list(void);
traverse_list(PNODE pHead);
typedef struct Node
{
int date;//数据域
struct Node * pNext;//指针域
}NODE,*PNODE;//NODE等价于struct Node,PNODE等价于struct Node *
int main(void)
{
PNODE pHead = NULL;//等价于struct Node * pHead = NULL;
pHead = creat_list();//创建一个非循环单链表,并将该链表的头指针传给遍历数组完成遍历。
traverse_list(pHead);//遍历链表输出
return 0;
}
PNODE creat_list(void)
{
int len;//用来存放有效节点的个数
int i;
int val;//用来临时存放用户输入的节点的值
PNODE pHead = (PNODE)malloc(sizeof(NODE));
if(NULL == pHead)
{
printf("分配失败,程序终止!\n");
exit(-1);
}//分配头节点的内存
PNODE pTai = pHead;//创建一个始终指向尾节点的节点
pTail-> = NULL;//把头指针的指针域赋值为空使得当用户输出的链表结点个数为0时,其依然可以进行输出。
printf("请输入您要生成的链表节点的个数:len = ");
scanf("%d",&len);
for(i=0;i<len;++i)
{
printf("请输入第%d个节点的值",i+1);
scanf("%d",val);
PNODE pNew = (PNODE)malloc(sizeof(NODE));
if(NULL == pHead)
{
printf("分配失败,程序终止!\n");
exit(-1);
}//分配新节点的内存
pNew->data = val;//将所需要的值赋值给新节点
pTail->pNext = pNew;//将新节点变为尾节点
pNew->pNext = NULL;//将新节点的指针域变成空,表示其为尾指针
pTail = pNew;//再生成一个指向尾节点的节点。
}
}
void traverse_list(PNODE pHead)
{
PNODE p = pHead->pNext;
while(NULL != p)
{
printf("%d",p->data);
p = p->pNext;
}
printf("\n");
}