单链表的初始化,整表创建,单个元素插入,单个元素删除,整表删除等操作
很早之前学的数据结构,放了很久后,以致对里面的一些操作都有些遗忘,故而再次温习了一下数据结构,并整理了一点儿笔记,放在这里和大家分享, 我的代码注释的已经很详细了,对于容易出错的地方我也都有标注,欢迎大家交流。
#include "stdafx.h" #include <stdio.h> #include <malloc.h> #include <stdlib.h> #include <time.h> #define OK 1 #define ERROR 0 //#define typedef int Status;//函数返回的状态值类型 typedef int ElemType; typedef struct Node { ElemType data; struct Node *next; } Node;//定义一个单链表存储结构 typedef struct Node *LinkList;//定义一个线性表,定义的是Node结构体的指针 //创建一个具有n个元素的单链线性表,采用头插法创建,注意:其中已经包含了初始化操作 //初始化链表,函数调用完毕后,L会指向一个空的链表,即会改变指针的值,所以要用*L //*L表示结构体指针的指针 Status List_Link_Create(LinkList *L,int n)//头插法 { LinkList p; *L =(LinkList) malloc(sizeof(Node));//产生一个头结点,并使得*L指向这个头结点,于是*L便是一个头指针,头指针是链表的必要元素 if (L == NULL) return ERROR; (*L)->next = NULL;//使得头指针指向的内容为空,建立一个带头结点的单链表 printf("请输出n个随机生成的数字:"); for (int i = 0; i < n; i++) { p = (LinkList)malloc(sizeof(Node));//生成新节点 p->data = rand() % 100 + 1;//随机生成100以内的数字 printf("%d", p->data); printf(" ");//字符与字符之间空格 p->next = (*L)->next; (*L)->next = p;//插入到表头 } printf("\n");//换行 return OK; } Status List_Link_Length(LinkList L)//求出单链表的长度 { int j = 0; LinkList p; p = L; while (p != NULL) { p = p->next; j++; } printf("单链表当前的长度=%d",j); return j; } //Status List_Link_Ini(LinkList L)//初始化一个线性单链表 //{ // L = (LinkList)malloc(sizeof(Node));//产生一个头结点,并使得L指向这个头结点,于是L便是一个头指针,头指针是链表的必要元素 // if (L == NULL) // return ERROR; // L->next = NULL;//使得头结点的指针域为空 // return OK; //} //销毁链表L,释放链表L申请的内存,使L的值重新变为NULL,所以会改变L的值,得用*L Status List_Link_Destory(LinkList *L) { LinkList p,q; p = (*L)->next; while (p) { q = p->next; free(p); p = q; } (*L)->next = NULL;//头节点的指针域为空 return OK; } Status List_Link_Insert(LinkList *L, int i, ElemType e)//在L中第i个元素之前插入新的数据元素e { int j; LinkList p, s; p = *L; j = 1; while (p&&j<i)//遍历寻找第i个节点 { p = p->next; ++j; } if (!p || j > i) return ERROR;//第i个元素不存在 s = (LinkList)malloc(sizeof(Node));//生成一个新节点 s->data = e; s->next = p->next;//将p的后继节点赋值给s的后继 p->next = s; printf("插入节点的元素的值为:%d\n", e); return OK;//插入成功 } //删除L的第i个数据元素,并用e返回其值 //注意这里是*e,而不是e,区别于插入当中的变量e Status List_Link_Delete(LinkList *L, int i, ElemType *e) { int j=1; LinkList p, q; p = *L; while (p->next&&j<i)//遍历寻找第i个元素 { p = p->next; ++j; } if (!(p->next) || j > i) return ERROR;//第i个元素不存在 q = p->next;//q表示即将被删除元素的节点 *e = q->data; p->next = q->next;//将q 后继赋值给p的后继 free(q);//q被利用完毕后,将q释放 printf("删除第%d个节点的元素值为:%d\n", i, *e); return OK; } Status List_Link_GetElem(LinkList L, int i, ElemType *e)//取出单链表L中的第i个元素,并通过*e返回 { int j; LinkList p; p = L->next;//找到单链表L中第一个节点 j = 1; while (p&&j < i) { p = p->next; ++j; } if (!p || j > i) return ERROR;//第i个元素不存在 *e = p->data;//取出第i个元素的数据域并传值给*e printf("被取出的元素的值为:"); printf("%d\n", *e); return OK; } int main()//测试函数 { LinkList L1; ElemType f,h; List_Link_Create(&L1, 6); List_Link_GetElem(L1, 3, &f); List_Link_Insert(&L1, 3, 15);//在链表的第3个节点之前插入元素15 List_Link_Delete(&L1, 3, &h);//删除链表的第3个节点的元素,并返回给h输出 return OK; } /*1.对于LinkList L : L是指向定义的node结构体的指针, 可以用->运算符来访问结构体成员, 即L->elem, 而(*L)就是个Node型的结构体了, 可以用点运算符访问该结构体成员, 即(*L).elem; 2.对于LinkList *L:L是指向定义的Node结构体指针的指针, 所以(*L)是指向Node结构体的指针, 可以用->运算符来访问结构体成员, 即(*L)->elem, 当然, (**L)就是Node型结构体了, 所以可以用点运算符来访问结构体成员, 即(**L).elem; 3.在链表操作中, 我们常常要用链表变量作物函数的参数, 这时, 用LinkList L还是LinkList *L就很值得考虑深究了, 一个用不好, 函数就会出现逻辑错误, 其准则是:如果函数会改变指针L的值, 而你希望函数结束调用后保存L的值, 那你就要用LinkList *L, 这样, 向函数传递的就是指针的地址, 结束调用后, 自然就可以去改变指针的值;而如果函数只会修改指针所指向的内容, 而不会更改指针的值, 那么用LinkList L就行了;*/
下面是我在vs2013中的测试结果:
请输出n个随机生成的数字:42 68 35 1 70 25
被取出的元素的值为:1
插入节点的元素的值为:15
删除第3个节点的元素值为:15
请按任意键继续. . .