小白学编程之单向链表(无头节点)
无头节点的单向链表相对于有头结点的单向链表来说相对复杂一点,如果理解了无头节点的单向链表,那么再去理解有头节点的单向链表就相对简单很多,废话不多说,之后从以下几个方面对无头节点单向链表进行分析。
typedef struct list{
int num; //存放数据
struct list * next; //记录节点地址
}STU;
先创建一个结构体STU,下面的操作都以此为基础
增加节点
每次增加节点都得申请空间
每次增加节点都是通过指针进行连接
1、头插法
STU* add_fun_by_head(STU* head){
STU* pnew = NULL;
pnew = (STU*)malloc(sizeof(STU));
if(NULL == pnew){
printf("创建链表失败\n");
exit(-1); //创建链表失败程序直接关闭
}
pnew -> next = head;
head = pnew;
}
2、尾插法
STU* pnew = NULL;
STU* tail - NULL;
STU* head = NULL;
pnew = (STU*)malloc(sizeof(STU));
if(NULL == head){
head = pnew;
tail = pnew;
}
else{
tail -> next = pnew;
tail = pnew;
}
注意事项:
头插法相对于尾插法而言,步骤相对简单,但是头插法插入是数据是反向存储的,输出的时候可以根据自己的需求进行修改
查看节点数(展示节点内容)
查看节点数量和展示节点内容其实步骤差不多
void show_fun(STU* head){
if(NULL == head){
printf("当前链表为空");
return ;
}
else{
while(NULL != head){
printf("%d\t", head -> num);
head = head -> next;
}
printf("\n");
}
return ;
}
注意事项:
如果想查看节点数量的话,可以在函数将函数的返回值设置成int类型(需要返回节点数量的情况),因为返回值是节点数量,在函数中定义一个整型变量将它的初始值定为0,即int sum = 0; 当head == NULL时说明该链表没有节点,节点数量为0。当head != NULL是在while循环中进行自增,当while循环结束sum的大小就是节点的数量,最后将返回值返回到主函数中return sum。如果不需要返回sum的值可以直接进行printf打印sum的数据,即printf(“%d\n”,sum);
删除节点
删除节点需要注意几个点:
1、判断是头删还是中间删(尾删)
2、删除节点之后一定要释放
3、判断链表是否为空,删除的节点是否符合标准
STU* del_fun(STU* head, int coust, iny x){//coust是节点个数,x是删除节点
if(NULL == head){
printf("此链表为空\n");
return head;
}
if(x < 1 || x > coust){
printf("没有此序号节点\n");
return head;
}
STU* pdel = head;
STU* pnew = head;
//头删
if(1 == x){
head = head -> next;
}
else{
//中间删(尾删)
for(int i = 1; i < x; i++){ //找到删除的节点
pdel = pdel -> next;
}
for(int j = 1; j < x - 1; j++){ //找到删除的前一个点
pnew = pnew -> next;
}
pnew -> next = pdel -> next;
}
free(pdel);
return head;
}
注意事项:
头删和中间删(尾删)是不一样的,因为头删改变了头节点的地址,所以在写函数的时候可以通过二级指针或者是返回新的头节点地址来正确表示链表内容,中间删(尾删)的头节点地址没有发生改变所以不影响最后的结果。最后需要注意的一点就是删除的节点一定要释放。
插入节点
插入节点同样需要注意几点:
1、插入新的节点,就需要给新的节点申请空间
2、插入的位置需要考虑是头插还是中间插(尾插)
3、判断链表是否为空,插入的位置是否在链表之间
STU* insert_fun(STU* head, int coust){
int x, y;
printf("插入到第几个节点");
scanf("%d", &x);
while(getchar != '\n');
printf("插入的数据");
scanf("%d", &y);
while(getchar() != '\n');
STU* p = head; //记录插入位置的前一个节点
STU* pnew = (STU*)malloc(sizeof(STU));
pnew -> num = y;
if(NULL == head){
printf("链表为空\n");
return head;
}
if(x < 1 || x < coust){
printf("没有此序号节点\n");
return head;
}
//头插
if(1 == x){
pnew -> next = head;
head = pnew;
}
else{
//中间插(尾插)
for(int i = 1; i < x -1; i++){
p = p -> next;
}
p -> next = pnew -> next;
pnew -> next = p;
}
return head;
}
注意事项:
插入需要判断头插或者是中间插(尾插),因为头插是会改变头节点的地址,还有插入之前一定要给需要插入的数值申请新的空间。
排序
以升序为例,升序可以分为三个步骤:
1、在原来的链表中找到最大值
2、删除原来链表中的最大值
3、在新链表中保存最大值
STU* find_max(STU* head){
STU* max = head;
while(NULL != head){
if(max -> num < head -> next){
max = head;
}
head = head -> next;
}
return max;
}
STU* del_fun_by_old(STU* head, STU* max){
STU* p = NULL; //记录max的前一个节点
p = head;
//如果max == head的话说明max是第一个节点,头删
if(max == head){
head = head -> next;
}
else{
//max如果不是头节点,那就需要借助前一个节点来删除max
while(p -> next != max){
p = p -> next;
}
p -> next = max -> next;
}
return head;
}
STU* add_fun_by_new(STU* pnew, STU* max){
max -> next = pnew;
pnew = max;
return pnew;
}
void sort_fun(STU* head){
STU* pmax = NULL;
STU* pnew = NULL;
while(NULL != head){
pmax = find_max(head);
head = del_fun_by_old(head, pmax);
pnew = add_fun_by_new(head, pmax);
}
show_fun(pnew);
}
注意事项:
排序的过程其实就是找最大值的过程,所以降序的过程和升序一样是找到最小值然后进行排序。在排序过程中删除旧的链表也要满足头删和中间删(尾删)的规则,因为头删的头节点是在改变的。在排序的过程中旧的链表在删除过程中不需要释放,因为在新的链表中是需要用到的,如果释放的话可能会导致结果出错。
总结
无节点单链表实际上就是对指针的熟练运用,增删改查也是在原来学习的c语言基础上进行操作。
(ps:上述代码纯手打有可能会有错误,但是思路是一样的)