目录
引用头指针的好处:
1.结点的定义和初始化单链表
2.判断单链表是否为空表
3.销毁单链表
4.清空单链表,头结点和头指针还在
5.求单链表表长
6.取单链表中指定位置的数据
7.按值查找,返回数据所在的地址,时间复杂度为O(n)编辑
8.按值查找,返回数据所在位置序号,时间复杂度为O(n)
9.在第pos个元素之前插入数据元素e
10.删除第i个结点,时间复杂度为O(1),因为不需要移动结点,只需修改指针
11.单链表的建立:头插法
12.单链表的建立:尾插法
13.合并两个链表
引用头指针的好处:
1.使得在链表的第一个位置上的操作和在表的其他位置上的操作一致。
2.空表和非空表的处理一致。
1.结点的定义和初始化单链表
#include //定义单链表的结点typedef struct Lnode{ //node是结点的英文 Elemtype data; //存放数据,Elemtype是虚拟的类型,真实情况这里可以是int、float等类型 struct Lnode* next; //存放指向下一个结点的指针,而这个指针的类型是struct Lnode*型}Lnode //将Lnode定义为struct Lnode型typedef struct Lnode* Linklist; //Linklist表示指向struct Lnode这个结构体的指针//L链表可以用Linklist L或Lnode *L表示, 指向某个结点的指针可以用Lnode *p或Linklist p表示//初始化单链表,L是一个指向单链表的指针void initlist(Linklist L){ L = (Linklist)malloc(sizeof(Lnode)); //头结点的类型为Lnode,头指针的类型为Linklist L->next = NULL;}//总结初始化://1.生成一个头结点(分配空间),将头指针L指向头结点,也就是头指针L赋值为头结点的地址//2.将头结点的指针域赋值为空NULL
2.判断单链表是否为空表
//判断单链表是否为空表int is_Emptylist(Linklist L){ //L指向的是头结点,头结点的指针域(L->next)为空,没有下一个结点,就是空表 if(L->next == 0) return 0; //是空表 else return 1; //不是空表}
3.销毁单链表
//销毁单链表void destroylist(Linklist L){ Lnode* p; //定义一个指针p来指向结点,而结点是Lnode型,所以p的类型为Lnode * while(L != NULL) //NULL是最后一个结点的指针域,所以当L为NULL时表示最后一个结点已经销毁了 { p = L; //将L的值赋给p,这样p也指向了L指向的结点 L = L->next; //将下一个结点的地址放在当前结点的指针域(L->next)里,这样L就指向了下一个结点 free(p); //销毁p指向的结点 }}//总结销毁单链表://1.引入一个指针p来指向结点//2.将L的值赋给p//3.将L指向下一个结点//4.销毁p指向的结点//5.将L的值赋给p,此时L不再指向头结点,而是下一个结点,所以p指向了下一个结点//6.将L指向下一个结点//重复p指向L结点,L指向下一个结点,销毁p指向的结点,p指向L结点.......直到L为NULL
4.清空单链表,头结点和头指针还在
//清空单链表,头结点和头指针还在void clearlist(Linklist L){ Lnode *p,*i; //引入两个指针变量指向结点,p指向当前要清除的结点,i指向p的下一个结点 p = L->next; //将p指向首元结点,从首元结点开始清空,因为要保留头结点 while(p != NULL) /*最后一个结点时,p->next(为NULL)赋给i,最后一个结点清空后 i(为NULL)赋给p,这样就表示整个表就清空了*/ { i = p->next; //i指向了下一个结点,这样上一个结点就可以清除了 free(p); //清除结点 p = i; //将p也指向i指向的结点 } L->next = NULL; //将头结点的指针域设为NULL}//总结清空单链表//1.将p指向首元结点(头结点的下一个结点)//2.将p结点的指针域(next)赋给i指针,使i指向p的下一个结点//3.清空p指针指向的结点//4.将i赋给p,使p指向i指向的结点//5.将p结点的指针域赋给i指针,使i再指向下一个结点,以此重复到p为NULL//6.将L指向的结点,也就是头结点的指针域设为NULL
5.求单链表表长
//求单链表表长int listlength(Linklist L){ Lnode* p; int i = 0; //用来计数,表示有几个结点,记得赋初值为0 p = L->next; //L->next指向的是首元结点,所以p指向首元结点 while(p != NULL) { i++; //第一次进循环,p已经是L->next了,如果p为null就代表没有首元结点,不会进入循环,此时i为0。 p = p->next; //将p指向下一个结点 } return i; //i就是表长}
6.取单链表中指定位置的数据
//取单链表中指定位置的数据Elemtype GetElemList(Linklist L, int pos) { Lnode* p; int k = 1; //k当做计数,初值为1,因为pos不是下标,所以不能为0 p = L->next; //从首结点开始找 while((p != NULL)&&(knext; k++; } if((p == NULL)||(k>pos)) //k>pos代表pos为0或负数,因为k初值为1。 return ERROR; //找不到 return p->data; //找到了会跳出循环,直接执行此语句将找到的值return回去。没找到会执行上面的if语句,不会执行此语句}
7.按值查找,返回数据所在的地址,时间复杂度为O(n)
//按值查找,返回数据所在的地址Lnode* SearchPos(Linklist L, Elemtype e){ Lnode* p; p = L->next; //从首元结点开始找 while((e != p->data)&&(p != NULL)) { p = p->next; } return p; //若找不到,就意味着p为NULL,此时返回NULL。找到了,返回的p就是数据所在地址}
8.按值查找,返回数据所在位置序号,时间复杂度为O(n)
//按值查找,返回数据所在位置序号int SearchNum(Linklist L, Elemtype e){ Lnode* p; int k = 1; //用来计数 p = L->next; //从首元结点开始找 while((p != NULL)&&(e != p->data)) { p = p->next; k++; } if(e == p->data) return k; //找到了,返回位置序号 else return ERROR; //else就是p为NULL,找不到}
9.在第pos个元素之前插入数据元素e
找到第pos-1个结点的时间复杂度为O(n),但之后,不管在第pos-1位置插入几个元素,时间复杂度都为O(1)。
//在第pos个元素之前插入数据元素evoid insertlist(Linklist L, Elemtype e, int pos){ int j = 1; //用来计数 Lnode* p, *s; //s用来指向新加入的结点 p = L; //从头结点开始,因为如果是在第1个元素之前插入,就应该是在头结点的后面插入 while((p != NULL)&&(jnext; //找下一个结点 j++; } if((p == NULL)||(j>pos)) /* pos不能为0或负数,所以j>pos是return ERROR。且pos不能大于表长,当pos大于表长的情况 就是循环结束后p==NULL,此时应return ERROR*/ return ERROR; s = (Linklist)malloc(sizeof(Lnode)); //给新插入的结点开辟空间,用来存放e s->data = e; //将e存放在新结点的数据域里 s->next = p->next; //将s的指针域指向原来的下一个结点 p->next = s; //将p的指针域指向新的结点}
10.删除第i个结点,时间复杂度为O(1),因为不需要移动结点,只需修改指针
找到第i-1个结点的时间复杂度为O(n),但之后不管插入几个元素,时间复杂度为O(1)。
void DeleteNode(Linklist L, int i){ Lnode *p, *s; int j = 0; p = L; while((p != NULL)&&(jnext; j++; } if((p == NULL)||(j>i-1)) return ERROR; s = p->next; //循环退出后p指向i-1个结点。这里是将第i个结点的地址暂存到s里 p->next = s->next; //将p指向的结点里面存下一个结点的地址 free(s); //删除s指向的结点}
11.单链表的建立:头插法
时间复杂度为O(n)
void createlist_H(Linklist L, int n) //n为创建的结点个数{ int i; Lnode* p; L = (Linklist)malloc(sizeof(Lnode)); //给头指针分配空间,也就是创建头结点 L->next = NULL; //最开始是空表,所以没有首元结点的地址L->next为NULL for(i = n; i>0; i--) //循环n次,创建n个结点 { p = (Lnode*)malloc(sizeof(Lnode)); //p作为新结点 scanf("%d",&p->data); //让用户输入数据 p->next = L->next; //将L后面的结点(头结点后的结点)连在p的后面 L->next = p; //将p插在头部作为首元结点 }}
12.单链表的建立:尾插法
时间复杂度为O(n)
void createlist_R(Linklist L, int n){ Lnode *r, *p; //r始终指向最后一个结点。p始终作为尾结点,插入到尾部 int i; L = (Linklist)malloc(sizeof(Lnode)); L->next = NULL; r = L; //首先是空表,最后一个结点也就是头结点,所以r=L for(i = 0; i data); //用户输入数据 p->next = NULL; //p作为尾结点所以指针域为NULL r->next = p; //将p插入到当前链表的尾部 r = p; //r始终指向尾结点 }}
13.合并两个链表
Linklist mergeTwoLists(Linklist list1, Linklist list2) { //创建一个新链表,此链表是带头结点的链表,list1和list2不是 Linklist list3 = new ListNode(); //创建一个新结点来指向链表3 ListNode* p3 = list3; //当list1遍历完或list2遍历完时,循环退出 while ((list1 != nullptr) && (list2 != nullptr)) { //list1的元素小 if (list1->val val) { //将list1指向的结点插入链表3 p3->next = list1; //将指向链表3的结点指向下一个结点 p3 = p3->next; //list1指向下一个结点 list1 = list1->next; } //list2的元素小 else { //将list2指向的结点插入链表3 p3->next = list2; //将指向链表3的结点指向下一个结点 p3 = p3->next; //list2指向下一个结点 list2 = list2->next; } } //循环结束,表示有一个链表已经遍历完了,而且因为是有序链表,所以直接将另一个链表的剩下的结点插入到链表3里 //或者用三目运算符:list3->next = p1 ? p1 : p2; if (list1 == nullptr) { p3->next = list2; } else { p3->next = list1; } return list3->next;}