线性表的链式存储,需要考虑链表是否带有头指针,是不是循环链表,是不是双链表
带有头结点的单链表
循环链表
双链表
- 不带有头结点的单链表,头指针head直接指向第一个结点,带有头结点的单链表,head指向头结点,头结点的存储结构和后续结点一样,但只使用了头结点的next指针域。
- 链表存储结构
typedef int datatype; typedef struct link_node{ datatype info; struct link_node *next; //指向下一个结点的link_node指针 }node;
- 基本运算实现函数
node *init(); //建立一个空的单链表 void display(node *head); //输出单链表中各个结点的值 node *find(node *head,int i); //在单链表中查找第i个结点的存放地址 node *insert(node *head, datatype x, int i); //在第i个结点后插入值为x的新结点 node *dele(node *head, datatype x); //在单链表中删除一个值为x的结点
- 具体实现函数
#include <stdio.h> #include <stdlib.h> #include <malloc.h> #include "slnklist.h" /************************************************************************/ /* 函数功能:建立一个空的单链表,单链表的初始化 */ /* 函数参数: 无 */ /* 函数返回值: 指向node类型变量的指针 */ /* 文件名: slnklist.c ,函数名:init() */ /************************************************************************/ node *init() { return NULL; } /************************************************************************/ /* 函数功能: 输出单链表中各个结点的值 */ /* 函数参数: 指向node类型变量的指针head*/ /* 函数返回值: 空 */ /* 文件名: slnklist.c ,函数名: display()*/ /************************************************************************/ void display(node *head) { node *p; p = head; if (!p) { printf("\n单链表是空的!"); } else { printf("\n单链表各个结点的值为:\n"); while(p) { printf("%5d",p->info); p = p->next; } } } /************************************************************************/ /* 函数功能: 在单链表中查找第i个结点的存放地址 */ /* 函数参数: 指向node类型变量的指针head,int型变量i */ /* 函数返回值: 指向node类型变量的指针 */ /* 文件名: slnklist.c ,函数名: find()*/ /************************************************************************/ node *find(node *head,int i) { int j=1; //结点从1开始计数 node *p = head; if (i<1) { return NULL; } while (p && i!=j) { p = p->next; j++; } return p; } /************************************************************************/ /* 函数功能: 单链表第i个结点后插入值为x的新结点 */ /* 函数参数: 指向node类型变量的指针head,datatype类型变量x,int型变量i */ /* 函数返回值: 指向node类型变量的指针 */ /* 文件名: slnklist.c ,函数名: insert()*/ /************************************************************************/ node *insert(node *head, datatype x, int i) { node *p,*q; q = find(head,i); //第i个结点的存放地址 if (!q && i != 0) { printf("\n找不到第%d个结点,不能插入%d!",i,x); } else { p = (node *)malloc(sizeof(node)); //分配空间 p->info = x; //设置新结点 if (i==0) //插入点作为单链表的第一个结点 { p->next = head; head = p; } else { p->next = q->next; q->next = p; } } return head; } /************************************************************************/ /* 函数功能: 在单链表中删除一个值为x的结点 */ /* 函数参数: 指向node类型变量的指针head,datatype类型变量x */ /* 函数返回值: 指向node类型变量的指针 */ /* 文件名: slnklist.c ,函数名: dele()*/ /************************************************************************/ node *dele(node *head, datatype x) { node *pre=NULL, *p; if (!head) { printf("单链表是空的!"); return head; } p = head; while (p && p->info!=x) //没有找到并且没有找完 { pre=p; p=p->next; } if (!pre && p->info==x)//要删除的是第一个结点 { head = p->next; } else pre->next = p->next; free(p); return head; }
- main验证函数
#include <stdio.h> #include <stdlib.h> #include "slnklist.h" void main(void) { node slnklist; node *p_slnklist; //初始化单链表 p_slnklist = init(); //利用插入方法建立一个链表 int getin; int num = 0; puts("输入插到链表末尾的值"); while ((scanf("%d", &getin))==1) { p_slnklist = insert(p_slnklist, getin,num); num++; } display(p_slnklist); //输出链表结点值 //查找第i个结点的存放地址 node *q; int i; puts("\n查找第i个结点的存放地址,输入i值"); while((scanf("%d", &i))==1) { q = find(p_slnklist, i); printf("第%d个结点值是%d,存放地址是%d\n", i, q->info, q); puts("查找第i个结点的存放地址,输入i值"); } //在第i个结点后面插入值为x的新结点 int x; puts("第i个结点后面插入值为x的新结点,输入i和x"); while((scanf("%d,%d", &i, &x))==2) { p_slnklist = insert(p_slnklist, x, i); display(p_slnklist); puts("\n第i个结点后面插入值为x的新结点,输入i和x"); } //在单链表中删除一个值为x的结点 puts("输入带删除的结点值x"); while ((scanf("%d", &x))==1) { p_slnklist = dele(p_slnklist, x); display(p_slnklist); puts("\n输入带删除的结点值x"); } system("pause"); }
运行结果
Ps:
- 单链表中查找第i个结点的存放地址时候,需要特别区分,i是否小于1,以及结点计数是否是从1开始
- 单链表第i个结点插入值为x的新结点,利用查找第i个结点地址的函数进行定位,需要注意,当find函数返回null时候,要判断是因为i为0,还是因为i超出当前链表大小,i值超出链表大小处理如下
if (!q && i != 0) { <span style="white-space:pre"> </span>printf("\n找不到第%d个结点,不能插入%d!",i,x); }
- 特别注意,插入结点作为链表第一个结点时候的处理
if (i==0) //插入点作为单链表的第一个结点 { p->next = head; head = p; } else { p->next = q->next; q->next = p; }
- 删除结点时候,需要判断链表是否为空;不为空时候,查找值为x的结点,并在查找过程中,保存该结点的前驱结点的指针;找到值为x的结点后,需要判断该结点是否为链表的第一个结点
if (!pre && p->info==x)//要删除的是第一个结点 { head = p->next; } else pre->next = p->next; free(p);