之前跟着朱老师写的一个带头结点的:https://blog.csdn.net/weixin_42072280/article/details/82722253
创建链表时有两种方法, 一种是先初始化(创建一个空链表),然后对这个空链表进行结点输入,创建链表;这个时候,由于已经有了头结点,将其传入,直接在头结点的后面进行操作; 另外一种是将初始化和创建链表放到一个函数里,这时候传入的链表就什么都没有,连头结点也没有;先创建头结点,让传入的指针指向头结点,接着继续输入数据; 也有的书上是什么都不传入,最后生成一个链表后传出这个链表。 |
插入找前驱(第k个位置的前驱) | 删除找前驱 |
//这个要从头结点开始 //如果在第1个位置插入,前驱就是头结点
for(pre = (*L); pre && i < k-1; i++){ |
//这个要从头结点开始 //如果要删除第1个位置的结点,前驱就是头结点
for(pre = (*L); pre->next && i < k-1; i++){ //寻找第k-1个结点 |
这几天写的这些个程序还有一个小缺陷,就是那个要修改链表数据的形参中,并不需要使用**L,用*L就可以;因为即使这个是传值,但是传的是指针的值,指针指向的内容是不变的。 |
#include
#include
typedef unsigned char boolean;
#define TRUE 1
#define FALSE 0
typedef int ElemType;
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode;
/*
//有些书上描述链表这样子描述,就是多定义了一个指向结点的指针而已
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode, *LinkList;
*/
void initLinkList(LNode **L); //初始化的意思是构造一个空链表
void createLinkList(LNode **L); //尾插法 由于之前已经初始化建立了空链表,所以这个函数传入的链表就直接加有效数据结点就可以了
void createLinkList2(LNode **L); //头插法 尾插法需要一个指针,始终指向末结点 头插法则不用
//输出链表
void printLinkListData(LNode *L);
//查找
ElemType getElem(LNode *L, int k); //找出带头结点单链表中第i个位置的结点的数据
LNode *getElem2(LNode *L, int k); //找出带头结点单链表中第i个位置的结点的指针
LNode *locateElem(LNode *L, ElemType x); //找到带头结点单链表中值为x结点的指针
//插入
void insertElem(LNode **L, int k, ElemType x); 在带头结点单链表中第i个位置上插入新结点
//这就意味着是前插,但是所有的前插都是找到该结点的前驱结点,然后再进行后插
//删除
void deleteElem(LNode **L, int k);
boolean isEmpty(LNode *L);
int lengthOfLinkList(LNode *L); //链表长度
int lengthOfLinkList(LNode *L){
int i = 0;
LNode *p;
for(p = L; p->next; i++){
p = p->next;
}
return i;
}
boolean isEmpty(LNode *L){
return (L->next == 0);
}
void deleteElem(LNode **L, int k){
LNode *pre = NULL; //被删除结点的前驱结点
LNode *p = NULL; //指向被删除结点
int i = 0;
/* 错了
//删除的找前驱和插入的找前驱不一样
//若要插入到表长的后一个元素,则可以找前驱到表长的最后一个元素
//但是删除表长的后一个元素,前驱就不能定位了
//找前驱结点
if(k < 1){
printf("您输入的位置有误!\n");
}
for(pre = (*L); pre && i < k-1; i++){ //寻找第k-1个结点
pre = pre->next; //这个要从头结点开始,因为是在找前驱,如果在第1个位置插入,前驱就是头结点
}
if(!pre){
printf("您输入的位置大于表长!\n");
}
p = pre->next; //p指向要删除的结点
pre->next = p->next;
free(p);
printf("删除完毕!\n");
*/
//找前驱结点
if(k < 1){
printf("您输入的位置有误!\n");
}
for(pre = (*L); pre->next && i < k-1; i++){ //寻找第k-1个结点
pre = pre->next; //这个要从头结点开始,因为是在找前驱,如果在第1个位置插入,前驱就是头结点
}
if(pre->next == 0){
printf("您输入的位置大于表长!\n");
}
p = pre->next; //p指向要删除的结点
pre->next = p->next;
free(p);
printf("删除完毕!\n");
}
//这就意味着是前插,但是所有的前插都是找到该结点的前驱结点,然后再进行后插
void insertElem(LNode **L, int k, ElemType x){
LNode *pre = NULL;
LNode *p = NULL; //指向新结点
int i = 0;
/*
//找前驱结点(这个方法是在已经有了getElem2这个函数的基础上进行的)
if(k < 1){
printf("您输入的位置有误!\n");
}else if(k == 1){ //}else if(k = 1){
pre = *L;
//printf("**\n");
}else{
pre = getElem2(*L, k-1);
//printf("*\n");
}
//printf("pre->data=%d\n", pre->data);
*/
//找前驱结点
if(k < 1){
printf("您输入的位置有误!\n");
}
for(pre = (*L); pre && i < k-1; i++){ //寻找第k-1个结点
pre = pre->next; //这个要从头结点开始,因为是在找前驱,如果在第1个位置插入,前驱就是头结点
}
if(!pre){
printf("您输入的位置大于表长!\n");
}
//为新结点开辟空间
p = (LNode *)malloc(sizeof(LNode));
p->data = x;
p->next = pre->next;
pre->next = p;
printf("插入完毕!\n");
}
//从单链表第一个结点开始,由前往后依次比较表中各结点数据域的值,若某结点数据域的值等于x,则返回该结点的指针;
//若整个单链表中没有这样的结点,则返回NULL
LNode *locateElem(LNode *L, ElemType x){
LNode *p = L->next;
while(p && p->data != x){
p = p->next;
}
return p; //返回值为x的结点的指针,如果没有这样的值,p=NULL,直接返回p即可
}
//在单链表中从第一个结点出发,顺指针next域逐个往下搜索,直到找到第k个结点为止,否则返回最后一个结点指针域NULL
LNode *getElem2(LNode *L, int k){
int i = 1; //计数器
LNode *p = L->next;
if(k < 1){
printf("您输入的位置有误!\n");
return ;
}
while(p && i < k){
p = p->next;
i++;
}
return p; //返回第k个结点的指针,如果k大于表长,p=NULL,直接返回p即可
}
ElemType getElem(LNode *L, int k){
int i = 1; //计数器
LNode *p = L->next;
if(k < 1){
printf("您输入的位置有误!\n");
return ;
}
while(p && i < k){ //寻找第k个结点
//printf("p->data=%d\n", p->data);
p = p->next;
i++;
}
if(!p){
printf("您输入的位置大于单链表中数据的长度!\n");
return;
}
return p->data;
}
void printLinkListData(LNode *L){
LNode *p;
p = L->next; //p指向第一个有效结点
while(p){
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
void createLinkList2(LNode **L){ //头插
LNode *p; //p指向新结点
ElemType data;
*L = (LNode *)malloc(sizeof(LNode));
(*L)->next = NULL;
printf("请输入数据:\n");
scanf("%d", &data);
while(data != 9999){ //输入9999表示结束
p = (LNode *)malloc(sizeof(LNode));
p->data = data;
p->next = NULL;
p->next = (*L)->next;
(*L)->next = p;
scanf("%d", &data);
}
}
//下面的这个创建链表的方法,是把初始化和创建链表放到了一起,即传入的链表什么都没有,连头指针都没有
void createLinkList(LNode **L){ //尾插 需要增加一个指针,指向末结点
LNode *p; //p指向新结点
LNode *q; //q始终指向末结点
ElemType data;
*L = (LNode *)malloc(sizeof(LNode));
(*L)->next = NULL;
q = *L;
printf("请输入数据:\n");
scanf("%d", &data);
while(data != 9999){ //输入9999表示结束
p = (LNode *)malloc(sizeof(LNode));
p->data = data;
p->next = NULL;
q->next = p;
q = p;
scanf("%d", &data);
}
}
/*
void createLinkList(LNode **L){
LNode *p; //p指向新结点
LNode *q = *L; //q始终指向末结点
ElemType data;
printf("请输入数据:");
scanf("%d", &data);
while(data != 9999){ //输入9999表示结束
p = (LNode *)malloc(sizeof(LNode));
p->data = data;
p->next = NULL;
q->next = p;
q = p;
scanf("%d", &data);
}
}
void initLinkList(LNode **L){
if(*L != NULL){
printf("该指针已经指向一个有效链表!\n");
}else{
*L = (LNode *)malloc(sizeof(LNode));
(*L)->next = NULL;
}
}
*/
void main(void){
//LNode list = {0}; //这样子的话,直接定义了一个结点,并且赋初值为0,就相当于定于了一个空链表
LNode *list = NULL; //这样子的话,只是定义了一个指针,并没有具体的开辟结点空间,所以需要初始化,构造一个空链表
createLinkList(&list);
printLinkListData(list);
//printf("第%d个地方的值为:%d\n", 2, getElem(list, 2));
//printf("第%d个地方的值为:%d\n", 2, (getElem2(list, 2))->data);
//printf("值为%d的结点的指针所指向的值为:%d\n", getElem(list, 2), (locateElem(list, getElem(list, 2)))->data);
//insertElem(&list, 6, 6);
//printLinkListData(list);
deleteElem(&list, 5);
printLinkListData(list);
printf("链表是否为空:%d\n", isEmpty(list));
printf("链表长度为:%d\n", lengthOfLinkList(list));
}