2022-9-11
看了单链表,尝试写了一点,有了一点心得;
英语还是得学,不然命名还得靠翻译;
不清楚怎么算标准的命名,只好直接填单词了;
不是科班的,自学难免犯错,如果发现错误请直接纠正,非常感谢;
初始化和插入等操作需要用二级指针;
#define error -1
typedef int ElemType
定义单链表
typedef struct LNode { //定义单链表 L就是头结点
ElemType data;
struct LNode* next;
}LNode, * LinkList;
//上面等同于下面
typedef struct LNode LNode;
typedef LNode *LinkList;
这里以前不是很清楚,为什么这么写,只记了个形式;
LNode强调是一个结点,LinkList强调其是一个链表,
LinkList L 和 LNode *L 是同一个东西,L就是一个指向链表开头的指针,找到L就能找到后续的结点;
结点L(代表整个链表)->第一个元素->第二个元素…第n个元素->NULL
L就是其头结点,可以添加指针Head标识
初始化
void InitList(LinkList* L) { //初始化,使用二级指针
*L = (LinkList)malloc(sizeof(LNode));
(*L)->next = NULL;
if ((*L) == NULL) {
printf("Initialize Failed\n");
}
else {
printf("success\n");
}
}
头插法建立链表
void Create_List_HeadInsert(LinkList* L) { //已有链表情况下,头插法插入数据(逆序)
int x;
scanf_s("%d", &x);
LNode* newnode;
while (x != 9999) {
LNode* newnode = (LNode*)malloc(sizeof(LNode));
newnode->data = x;
newnode->next = (*L)->next;
(*L)->next = newnode;
scanf_s("%d", &x);
}
}
这种方法建立的链表与输入顺序是相反的,输入1,2,3,4,建立的链表是4,3,2,1;
判断是否为空
思路是判断L后面的结点是否存在
L是否存在
L->next是否存在
int Empty_list(LinkList L) { //判空
if (L == NULL) {
return error;
}
else {
if (L->next == NULL) {
return 1;
}
else {
return 0;
}
}
}
好像可以使用bool类型的函数,不过要加#include <stdbool.h>
表长
思路是遍历整个链表,每经过一个结点就使计数器+1
L结点不计入在内
前提是L存在
int ListLength(LinkList L) { //表长
int count = 0;
if (L != NULL) {
LNode* p = L->next;
while (p != NULL) {
count++;
p = p->next;
}
return count;
}
else {
printf("LinkList Not Found! error=");
return error;
}
}
遍历
思路是新建一个指针tl_tmp指向第一个元素,当该结点后还有结点是打印其data域的值,即tl_tmp != NULL
void Traverse_List(LinkList L) { //遍历含尾节点的全部节点的data域
if (L == NULL) {
printf("ERROR\n");
}
else {
if (L->next != NULL) {
LNode* tl_tmp = L->next;
while (tl_tmp != NULL) {
printf("%p,%d,%p\n", tl_tmp, tl_tmp->data, tl_tmp->next); //按 结点地址, data域值, next域地址 顺序打印
tl_tmp = tl_tmp->next;
}
printf("Traverse Completed\n");
}
else { //L->next == NULL则为空表
printf("EMPTY LIST\n");
}
}
}
增
向链表中插入元素
void ListInsert(LinkList* L, int i, ElemType e) { //在第i个位置插入e
if ((*L) == NULL) {
printf("ERROR!\n");
}
if (i <= ListLength(*L) + 1 && i >= 0) {
LNode* p = (LNode*)malloc(sizeof(LNode));
p->data = e;
LNode* q = *L;
for (int x = 1; x < i; x++) {
q = q->next;
}
p->next = q->next;
q->next = p;
}
else {
printf("Invalid Input\n");
}
}
malloc和free一般是成对出现的,但插入新结点时需要新分配一块内存,此时需要保留这块这块内存所以暂时不用free,待使用完链表后销毁链表,所有结点都会被释放
删
删除指定位置的结点并用e返回被删除结点的data域的值
思路是新建指针pre_p 和 p,用p指示第i个结点,pre_p 指示 p的前一个结点以便连接p前后两个结点
ElemType ListDelete(LinkList *L, int i) { //删除第i个元素并返回被删除元素的值
if ((*L) == NULL) {
printf("LinkList Not Found! error=");
return error;
}
else {
if ((*L)->next == NULL) {
printf("Empty LinkList! error=");
return 0;
}
else
{
LNode* pre_p = *L;
for (int x = 1; x < i; x++) {
pre_p = pre_p->next;
}
LNode* p = pre_p->next;
pre_p->next = p->next;
p->next = NULL;
int e = p->data;
free(p);
p = NULL;
return e;
}
}
}
删除结点需要释放掉被删除结点的内存,所以在结束时应当free该结点,并使指针指空
删除指定结点
void NodeDelete(LinkList L, LNode *p) { //删除指定结点(结点必须是L中的结点)
if (L == NULL) {
printf("LinkList Not Found!");
}
else {
if (L->next == NULL) {
printf("Empty LinkList! error=");
return 0;
}
else
{
LNode* pre_p = *L;
while (pre_p != NULL && pre_p->next != p) {
pre_p = pre_p->next;
}
LNode* p = pre_p->next;
if (p->next != NULL) {
pre_p->next = p->next;
p->next = NULL;
}
else {
pre_p->next = NULL;
}
free(p);
p = NULL;
}
}
}
改
思路是新建指针标识第一个元素,然后移动至第i个元素并用e修改其data域的值
void ListRevise(LinkList *L, int i, ElemType e) {//修改第i位结点的值
if ((*L) == NULL || i <= 0 || i > ListLength(*L) || (*L)->next == NULL) {
printf("Invalid Input");
}
else {
LNode* p = *L;
for (int x = 0; x < i; x++) {
p = p->next;
}
p->data = e;
}
}
查
按值查找
思路是遍历整个链表同时比较data域的值,在找到符合要求的第一个结点时返回其地址
LNode* LocateElem(LinkList L, ElemType e) { //查找结点中第一个data域为e的结点并返回该结点指针(按值查找)
if (L == NULL) {
printf("LinkList Not Found!");
return NULL;
}
else {
if (L->next == NULL) {
printf("Empty List!");
return NULL;
}
else {
LNode* tmp = L->next;
while (tmp->data != e && tmp != NULL) {
tmp = tmp->next;
}
if (tmp == NULL) {
printf("Element Not Found!");
}
else {
return tmp;
}
}
}
}
按位查找
思路是新建一个指针标识第一个元素,然后移动至第i个元素并返回其data域的值
ElemType GetElem(LinkList L, int i) { //按位查找表L中第i个元素的值并返回,空表返回0,无效值返回-1
if (L == NULL) {
printf("Invallid Input! error=");
return error;
}
else {
if (i <= ListLength(L) && i>0) {
LNode* tmp = L->next;
for (int x = 1; x < i; x++) {
tmp = tmp->next;
}
return tmp->data;
}
else {
printf("Invalid Input! error=");
return 0;
}
}
按位返回结点
LNode* GetNode(LinkList L,int i) {//按位返回结点
if (L == NULL) {
printf("Invallid Input! error=");
return error;
}
else {
if (i <= ListLength(L) && i>0) {
LNode* tmp = L->next;
for (int x = 1; x < i; x++) {
tmp = tmp->next;
}
return tmp;
}
else {
printf("Invalid Input! error=");
return 0;
}
}
}
按值返回位置
思路是遍历同时计数至第一个值为e的结点,返回计数器的值
int LocateElem_Sequence(LinkList L, ElemType e) { //返回给定值的次序,返回值-1:不存在L;0:空表或未找到
if (L == NULL) {
printf("Invalid Input! error=");
return error;
}
else {
if (L->next == NULL) {
return 0;
}
else {
int count = 1;
LNode* tmp = L->next;
while (tmp->data != e) {
if (tmp->next != NULL) {
tmp = tmp->next;
count++;
}
else {
if (tmp->data == e) {
return count;
}
else {
return 0;
}
}
}
}
}
}
销毁链表
思路是逐步销毁L->next结点直至全部为空
void ListDestroy(LinkList* L) { //销毁链表并释放空间
LNode* p = *L;
while (*L) {
p = (*L)->next;
free(*L);
(*L) = p;
}
if (!(*L)) {
printf("Destroy Completed\n");
}
}
复制链表
思路是逐个读取L中结点的data域的值,并插入L_copy 中
void LinkListCopy(LinkList L, LinkList* L_copy) {//复制链表,使用前先创建一个新链表L_copy接收复制的数据域
if (L == NULL || L->next == NULL || (*L_copy) == NULL) {
printf("Can Not Copy!");
}
else {
LNode* p = L->next;
int i = 1;
while (p != NULL) {
ListInsert((*L_copy), i, p->data);
p = p->next;
i++;
}
}
}
交换结点
(不改变原来的地址,仅改变次序)
思路是先用指针pre_a和 pre_b标识a,b两个结点的前一个结点的位置,再从链表中取出两个元素互换次序后再插入对应位置
void ListNode_Swap(LinkList *L, LNode* a, LNode* b) { //交换结点
if (a == NULL || b == NULL) {
printf("error!");
}
else {
if (a != b) {
LNode* pre_a = *L;
while (pre_a->next != a) {
pre_a = pre_a->next;
}
pre_a->next = a->next;
a->next = NULL;
LNode* pre_b = *L;
while (pre_b->next != b) {
pre_b = pre_b->next;
}
pre_b->next = b->next;
b->next = NULL;
a->next = pre_b->next;
pre_b->next = a;
b->next = pre_a->next;
pre_a->next = b;
}
}
}
逆置链表
思路有三种:
一种是不断交换两端的元素,如再一个有5个元素的链表中,1和5换,2和4换,只要一直调用交换结点的函数即可,不过很慢,不推荐
LinkList LinkListInverted(LinkList L) {
if (L == NULL || L->next == NULL) {
printf("Inverted Failed");
}
else {
int length = ListLength(L);
for (int i = 0; i < length / 2; i++) {
ListNode_Swap(L, GetNode(L, i+1),GetNode( L,length - i));
}
return L;
}
}
一种是使用一个指针指示表尾,并获取其data域的值,然后用头插法将值插入一个新的空表内,头插法插入数据是倒着插入的,所以倒着读取再插入即可获取逆置的链表,也可以只使用原本的空间,比交换结点要快。
LinkList LinkListInverted_a(LinkList L,LinkList L_inverted) {//逆置链表,但不使用原先的链表,创建一个新链表接收逆置的数据域,逆置的链表返回为L_inverted
if(L == NULL || L->next == NULL || L_inverted == NULL) {
printf("Inverted Failed");
}
else {
LNode* p = L->next;
while (p != NULL) {
LNode* q = (LNode*)malloc(sizeof(LNode));
q->data = p->data;
q->next = L_inverted->next;
L_inverted->next = q;
p = p->next;
}
return L_inverted;
}
}
void LinkListInverted(LinkList *L) { //逆置链表,使用原来的空间
LNode* p = (*L)->next;
LNode* q = p->next;
(*L)->next = NULL;
while (p != NULL) {
p->next = (*L)->next;
(*L)->next = p;
p = q;
if (q != NULL) {
q = q->next;
}
}
}
学习心得
malloc与free一般成对出现,分配空间后仍需要的结点不用free,不需要的应该及时free,并使其指针指空;
读取到被free的指针会报错,一般是0xfffffffffffffff7;
读取到next域为NULL的结点后强行读取其next域,也会报错0xfffffffffffffff7;
LinkList是一个指针,LNode是一个结点;
内存空间就像是一片沙滩,指针可以帮助找到需要的那一粒沙子,所以需要标识的结点应提前用指针标识方便寻找;
命名时应注意大小写使易于阅读;
凡是涉及链表本身的操作都应使用二级指针;
这里是全部的代码
加上#include "LinkList.h"即可使用
#ifndef _LinkList_H_
#define _LinkList_H_
typedef int ElemType; //定义data域元素类型
#define error -1
#define OK 1
typedef struct LNode { //定义单链表
ElemType data;
struct LNode* next;
}LNode, * LinkList;
void InitList(LinkList* L) { //初始化,使用二级指针
*L = (LinkList)malloc(sizeof(LNode));
(*L)->next = NULL;
if ((*L) == NULL) {
printf("Initialize Failed\n");
}
else {
printf("success\n");
}
}
int Empty_list(LinkList L) { //判空
if (L == NULL) {
return error;
}
else {
if (L->next == NULL) {
return 1;
}
else {
return 0;
}
}
}
void Create_List_HeadInsert(LinkList* L) { //已有链表情况下,头插法插入数据(逆序)
int x;
scanf_s("%d", &x);
LNode* newnode;
while (x != 9999) {
LNode* newnode = (LNode*)malloc(sizeof(LNode));
newnode->data = x;
newnode->next = (*L)->next;
(*L)->next = newnode;
scanf_s("%d", &x);
}
}
LinkList Create_List_TailInsert(LinkList L) { //已有链表情况下,尾插法插入数据
int x;
scanf_s("%d", &x);
LNode* t = L;
while (x != 9999) {
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = x;
s->next = t->next;
t->next = s;
t = t->next;
scanf_s("%d", &x);
}
return L;
}
int ListLength(LinkList L) {//表长
int count = 0;
if (L != NULL) {
LNode* p = L->next;
while (p != NULL) {
count++;
p = p->next;
}
return count;
}
if (!L) {
printf("LinkList Not Found! error=");
return error;
}
}
void Traverse_List(LinkList L) { //遍历含尾节点的全部节点的data域
if (L == NULL) {
printf("ERROR\n");
}
else {
if (L->next != NULL) {
LNode* tl_tmp = L->next;
while (tl_tmp != NULL) {
printf("%p,%d,%p\n", tl_tmp, tl_tmp->data, tl_tmp->next);
tl_tmp = tl_tmp->next;
}
printf("Traverse Completed\n");
}
else {
printf("EMPTY LIST\n");
}
}
}
void Traverse_a(LinkList L) {//递归法遍历
LNode* p = L;
if (p == NULL) {
return;
}
printf("%p,%d,%p\n", p, p->data, p->next);
return Traverse_a(p->next);
}
void ListRevise(LinkList L, int i, ElemType e) {//修改第i位结点的值
if (L == NULL || i <= 0 || i > ListLength(L) || L->next == NULL) {
printf("Invalid Input");
}
else {
LNode* p = L;
for (int x = 0; x < i; x++) {
p = p->next;
}
p->data = e;
}
}
void ListInsert(LinkList* L, int i, ElemType e) { //在第i个位置插入e
if ((*L) == NULL) {
printf("ERROR!\n");
}
if (i <= ListLength(*L) + 1 && i >= 0) {
LNode* p = (LNode*)malloc(sizeof(LNode));
p->data = e;
LNode* q = *L;
for (int x = 1; x < i; x++) {
q = q->next;
}
p->next = q->next;
q->next = p;
}
else {
printf("Invalid Input\n");
}
}
LNode* LocateElem(LinkList L, ElemType e) { //查找结点中第一个data域为e的结点并返回该结点指针(按值查找)
if (L == NULL) {
printf("LinkList Not Found! error=");
return NULL;
}
else {
if (L->next == NULL) {
printf("Empty List!");
return NULL;
}
else {
LNode* tmp = L->next;
while (tmp->data != e && tmp != NULL) {
tmp = tmp->next;
}
if (tmp == NULL) {
printf("Element Not Found!");
}
else {
return tmp;
}
}
}
}
int LocateElem_Sequence(LinkList L, ElemType e) { //返回给定值的次序,返回值-1:不存在L;0:空表或未找到
if (L == NULL) {
printf("Invalid Input! error=");
return error;
}
else {
if (L->next == NULL) {
return 0;
}
else {
int count = 1;
LNode* tmp = L->next;
while (tmp->data != e) {
if (tmp->next != NULL) {
tmp = tmp->next;
count++;
}
else {
if (tmp->data == e) {
return count;
}
else {
return 0;
}
}
}
}
}
}
ElemType GetElem(LinkList L, int i,ElemType *e) { //按位查找表L中第i个元素的值并返回,空表返回0,无效值返回-1,L不存在返回-2
if (L == NULL) {
printf("Invallid Input! error=");
return -2;
}
if (L->next == NULL) { //判断是否为空表
printf("Invallid Input! error=");
return 0;
}
else {
if (i <= ListLength(L) && i > 0) {
LNode* tmp = L->next;
for (int x = 1; x < i; x++) {
tmp = tmp->next;
}
*e = tmp->data;
return OK;
}
else {
printf("Invalid Input! error=");
return -1;
}
}
}
LNode* GetNode(LinkList L, int i) {//按位返回结点
if (L == NULL) {
printf("Invallid Input! error=");
return error;
}
else {
if (i <= ListLength(L) && i > 0) {
LNode* tmp = L->next;
for (int x = 1; x < i; x++) {
tmp = tmp->next;
}
return tmp;
}
else {
printf("Invalid Input! error=");
return 0;
}
}
}
ElemType ListDelete(LinkList L, int i) { //删除第i个元素并返回被删除元素的值
if (L == NULL) {
printf("LinkList Not Found! error=");
return error;
}
else {
if (L->next == NULL) {
printf("Empty LinkList! error=");
return 0;
}
else
{
LNode* pre_p = *L;
for (int x = 1; x < i; x++) {
pre_p = pre_p->next;
}
LNode* p = pre_p->next;
pre_p->next = p->next;
p->next = NULL;
int e = p->data;
free(p);
p = NULL;
return e;
}
}
}
void NodeDelete(LinkList* L, LNode* p) { //删除指定结点(结点必须是L中的结点)
if ((*L) == NULL) {
printf("LinkList Not Found!");
}
else {
if ((*L)->next == NULL) {
printf("Empty LinkList! error=");
return 0;
}
else
{
LNode* pre_p = *L;
while (pre_p != NULL && pre_p-> != p) {
pre_p = pre_p->next;
}
LNode* p = pre_p->next;
if (p->next != NULL) {
pre_p->next = p->next;
p->next = NULL;
}
else {
pre_p->next = NULL;
}
free(p);
p = NULL;
}
}
}
void ListDestroy(LinkList* L) { //销毁链表并释放空间
LNode* p = *L;
while (*L) {
p = (*L)->next;
free(*L);
(*L) = p;
}
if (!(*L)) {
printf("Destroy Completed\n");
}
}
void ListNode_Swap(LinkList L, LNode* a, LNode* b) { //交换结点
if (a == NULL || b == NULL) {
printf("error!");
}
else {
if (a != b) {
LNode* pre_a = L;
while (pre_a->next != a) {
pre_a = pre_a->next;
}
pre_a->next = a->next;
a->next = NULL;
LNode* pre_b = L;
while (pre_b->next != b) {
pre_b = pre_b->next;
}
pre_b->next = b->next;
b->next = NULL;
a->next = pre_b->next;
pre_b->next = a;
b->next = pre_a->next;
pre_a->next = b;
}
}
}
LinkList LinkListInverted(LinkList L) { //逆置链表
if (L == NULL || L->next == NULL) {
printf("Inverted Failed");
}
else {
int length = ListLength(L);
for (int i = 0; i < length / 2; i++) {
ListNode_Swap(L, GetNode(L, i + 1), GetNode(L, length - i));
}
return L;
}
}
LinkList LinkListInverted_a(LinkList L, LinkList L_inverted) {//逆置链表,但不使用原先的链表,创建一个新链表接收逆置的数据域,逆置的链表返回为L_inverted
if (L == NULL || L->next == NULL || L_inverted == NULL) {
printf("Inverted Failed");
}
else {
LNode* p = L->next;
while (p != NULL) {
LNode* q = (LNode*)malloc(sizeof(LNode));
q->data = p->data;
q->next = L_inverted->next;
L_inverted->next = q;
p = p->next;
}
return L_inverted;
}
}
void LinkListInverted(LinkList *L) { //逆置链表
LNode* p = (*L)->next;
LNode* q = p->next;
(*L)->next = NULL;
while (p != NULL) {
p->next = (*L)->next;
(*L)->next = p;
p = q;
if (q != NULL) {
q = q->next;
}
}
}
void LinkListCopy(LinkList L, LinkList* L_copy) {//复制链表,使用前先创建一个新链表L_copy接收复制的数据域
if (L == NULL || L->next == NULL || (*L_copy) == NULL) {
printf("Can Not Copy!");
}
else {
LNode* p = L->next;
int i = 1;
while (p != NULL) {
ListInsert((*L_copy), i, p->data);
p = p->next;
i++;
}
}
}
#endif // !_LinkList_H_