写给初学者的数据结构——初识链表(带头结点)(C++实现)
学习链表,这篇文章就够了。
目录
6、相关文章(Java和标准C实现)
全文一共3174字,准备、理解、操作大约需要40分钟的阅读时间,感谢遇见!
1、准备:
1、C语言允许用户自己建立由不同类型数据组成的结合型的数据结构,它成为结构体。如:
[code]struct student //一个记录学生信息的结构体 { int stid; //学号 char name[20]; //姓名 char sex; //性别 int score; //分数 };
2、定义结构体变量、结构体指针。如:
[code]struct student temp; //结构体变量 struct student *pt; //结构体指针
3、C允许程序设计者用一个简单的名字代替复杂的类型形式。如:
[code]typedef struct student std; //用std代替struct student
4、void *malloc(unsigned int size)
作用:在内存的动态存储区中分配一个长度为size的连续空间,返回分配区域的第一个字节的地址。如果此函数未能成功执行,则返回空指针(NULL).
理解:void* 不是“指向任何数据类型”,而是“不指向确定类型”。
如:
[code]int *p; p = (int)malloc(9*sizeof(int));
说明:1、是不是和“ int p[9] ”很像呢?2、这里为int的强制转换。3、这里的强制转换只是产生了一个临时的中间值赋给p,并没有实际改变malloc函数本身的类型。
5、引用
C和C++使用&符号来指示变量的地址。C++给&符号赋予了另一个含义将其用来声明引用。引用经常被用作函数参数,使得函数中的变量名成为调用程序中的变量的别名。(好好理解这句话)
再体会一下:
[code]int main() { int a = 5; add(a); printf("%d\n",a); add1(a); printf("%d\n",a); return 0; } void add(int a) { a = a + 1; } void add1(int &a) { a = a + 1; }
6、函数指针。如:
[code]Status(*compare)(ElemType,ElemType)
第1对圆括号把*和compare括起来,表明compare是一个指向函数的指针。因此,(*compare)是一个参数列表为(ElemType,ElemType)、返回类型为Status的函数。
作为函数的参数是数据指针最常见的用法之一,函数指针亦是如此。
2、理解:
1、什么是链表?网上有一个非常精辟的比喻:链表就像一列火车,火车头是头指针,一个车厢是一个结点。
2、链表的每一个结点都应该包括两个部分:用户需要用的实际数据和下一个结点的地址。
3、
3、操作:(为了更好的理解,请允许我先代入C++的“引用”)
0、初始
[code]#include<stdio.h> #include<math.h> //储存着OVERFLOW,值为3 #include<stdlib.h> typedef int ElemType; typedef int Status; typedef int Boolean; #define TRUE 1 #define FALSE 0 #define OK 1 #define ERROR 0 #define INFEASIBLE -1 struct LNode { ElemType data; struct LNode *next; }; typedef struct LNode* LinkList;
1、建立(也就是令一个指针指向一个“结点“大小的存储空间的过程)
[code]Status InitList(LinkList &L) //操作结果: 构造一个空的线性表L { L = (LinkList)malloc(sizeof(struct LNode)); //产生头结点,并使L指向此头结点 if(!L) //存储分配失败 { exit(OVERFLOW); } L->next = NULL; return OK; }
2、插入、删除、查找、替换
[code]Status ListInsert(LinkList L,int i,ElemType e) //在带头节点的单链线性表L中第i个位置之前插入元素e { int j = 0; LinkList p = L,s; while(p && j<i-1) //寻找第i-1个结点,令p指向它。p指向第一个结点的时候,j为1. { p = p->next; j++; }//这里有两个终止条件,一个是链表遍历完了,一个是找到了 if(!p || j>i-1) //如果遍历完了没找到 { return ERROR; } s = (LinkList)malloc(sizeof(struct LNode)); //生成新节点 //插入L中 s->data = e; s->next = p->next; p->next = s; return OK; } Status ListDelete(LinkList L,int i,ElemType &e) //在头结点的单链线性表l中,删除第i个元素,并由e返回其值 { int j=0; LinkList p=L,q; while(p->next && j<i-1) //寻找第i个结点,并令p指向其前趋,即第i-1个结点。p指向第一个结点的时候,j为1. { p = p->next; j++; }//与ListInsert对比一下?只有while里面的p->next不同 if(!p->next || j>i-1) //删除位置不合理 { return ERROR; } q = p->next; //删除并释放节点 p->next = q->next; e = q->data; free(q); return OK; } Status DeleteElem(LinkList L,ElemType e) //删除表中值为e的元素,并返回TRUE;如无次元素,则返回FALSE { LinkList p = L, q; while(p) { q = p->next; if(q && q->data==e) { p->next = q->next; free(q); return TRUE; } p = q; } return FALSE; } int LocateElem(LinkList L,ElemType e,Status(*compare)(ElemType,ElemType)) { //初始条件:线性表L已存在,compare()是数据元素判定函数(满足为1,否则为0) //操作结果: 返回L中第1个与e满足关系compare()的数据元素的位序. // 若这样的元素不存在,则返回值为0 int i = 0; LinkList p = L->next; while(p) { i++; if(compare(p->data,e)) //找到这样的数据元素 { return i; } p = p->next; } return 0; } Status comp(ElemType c1,ElemType c2) { //数据元素判定函数(相等为TRUE,否则为FALSE) if(c1==c2) { return TRUE; } else { return FALSE; } } Status ReplaceElem(LinkList L,int i,ElemType e) //用e取代表l中第i个元素的值 { LinkList p = L; int j = 0; while(p->next && j<i) //j为1的时候p指向第一个结点 { j++; p = p->next; }//这里有两个终止条件,一个是p指向空,即链表遍历完毕。另一个是j==i,即找到对应元素。 if(j==i) { p->data = e; return OK; } else //表中不存在第i个元素 { return ERROR; } }
3、销毁、清空、判空、表长
[code]Status DestoryList(LinkList &L) { //初始条件: 线性表L已存在。 //操作结果: 销毁线性表L LinkList q; //L指向最后一个元素时结束,L指向了(n+1)次 while(L) { q = L->next; free(L); L = q; } return OK; } Status ClearList(LinkList L) { //初始条件: 线性表L已存在. //操作结果: 将L重置为空表 LinkList p,q; p = L->next; //p指向第一个结点 while(p) //没到表尾 { q = p->next; free(p); p = q; } L->next = NULL; return OK; } Status ListEmpty(LinkList L) { //初始条件:线性表L已存在。 //操作结果:若为空表,则返回FAlSE; if(L->next) //非空 { return FALSE; }else { return TRUE; } } int ListLength(LinkList L) { //初始条件: 线性表L已存在。 //操作结果: 返回L中数据元素的个数 int i = 0; LinkList p = L->next; //p指向第一个结点 while(p) //没到表尾 { i++; p = p->next; } return i; }
4、 求值、前驱、后继、遍历
[code]Status GetElem(LinkList L,int i,ElemType &e) { //L为带头结点的单链表的头指针 //当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR int j = 1; //j为计数器 LinkList p = L->next ; //p指向第一个结点 while(p && j<i)//p指向第2个结点时,j为2。所以j为i时,p指向第i个结点 { p = p->next; j++; } if(!p || j>i) //第i个元素不存在 { return ERROR; } e = p->data; //取第i个元素 return OK; } Status GetFirstElem(LinkList L,ElemType &e) //返回表头元素的值 { LinkList p = L->next; if(!p) { return ERROR; } else { e = p->data; } return OK; } Status PriorElem(LinkList L,ElemType cur_e,ElemType &pre_e) { //初始条件: 线性表L已存在 //操作结果: 若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱。 // 返回OK;否则操作失败,pre_e无定义,返回INFEASIBLE LinkList q,p = L->next; //p指向第一个结点 while(p->next) //p所指结点有后继 { q = p->next; //q为p的后继,实质是从第二个判到最后一个 if(q->data==cur_e) { pre_e = p->data; return OK; } p = q; } return INFEASIBLE; } Status NextElem(LinkList L,ElemType cur_e,ElemType &next_e) { //初始条件:线性表L已存在 //操作结果: 若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继 // 返回OK;否则操作失败,next_e无定义,返回INFEASIBLE LinkList p = L->next; //p指向第一个结点 while(p->next) //p所指结点有后继 { if(p->data==cur_e) //实质是从第一个判到倒数第二个 { next_e = p->next->data; return OK; } p = p->next; } return INFEASIBLE; } void visit(ElemType c) { printf("%d ",c); } Status ListTraverse(LinkList L,void(*vi)(ElemType)) { //初始条件: 线性表L已存在 //操作结果: 依次对L的每个数据元素调用函数vi().一旦vi()失败,则操作失败 LinkList p = L->next; while(p) { vi(p->data); p = p->next; } printf("\n"); return OK; }
5、头插、尾插、头删、尾删
[code]Status HeadInsert(LinkList L,ElemType e) { //初始条件:线性表L已存在。 //操作结果: 在L的头部插入新的数据元素e,作为链表的第一个元素 LinkList s; s= (LinkList)malloc(sizeof(struct LNode)); //生成新结点 s->data = e; //给结点赋值 s->next = L->next; //插在表头 L->next = s; return OK; } Status EndInsert(LinkList L,ElemType e) { //初始条件:线性表L已存在. //操作结果:在L的尾部插入新的数据元素e,作为链表的最后一个元素 LinkList p = L; while(p->next) //使p指向表尾元素 { p = p->next; } p->next = (LinkList)malloc(sizeof(struct LNode)); //在表尾生成新节点 p->next->data = e; //给新节点赋值 p->next->next = NULL; //表尾 return OK; } Status DeleteFirst(LinkList L,ElemType &e) { //初始条件: 线性表L已存在,且有不少于1个元素 //操作结果: 删除L的第一个数据元素,并由e返回其值 LinkList p=L->next; if(p) { e = p->data; L->next = p->next; free(p); return OK; } else { return ERROR; } } Status DeleteTail(LinkList L,ElemType &e) { //初始条件: 线性表L已存在,且有不少于1个元素 //操作结构: 删除L的最后一个数据元素,并用e返回其值 LinkList p=L,q; if(!p->next) //链表为空 { return ERROR; } while(p->next) { q = p; p = p->next; } q->next = NULL; //新尾结点的next域设为NULL e = p->data; free(p); return OK; }
6、非降序插入、非升序插入、非降序建表、非升序建表
[code]void InsertAscend(LinkList L,ElemType e) { //初始条件: 按非降序排列的线性表L已存在。 //操作结果: 在L中按非降序插入新的数据元素e LinkList q=L,p=L->next; while(p&&e>p->data) { q = p; p = p->next; } q->next = (LinkList)malloc(sizeof(struct LNode)); //插在q后 q->next->data = e; q->next->next = p; } void InsertDescend(LinkList L,ElemType e) { //初始条件:按非升序排列的线性表L已存在. //操作结果:在L中按非升序插入新的数据元素e LinkList q = L,p = L->next; while(p && e<p->data) { q = p; p = p->next; } q->next = (LinkList)malloc(sizeof(struct LNode)); //插在q后 q->next->data = e; q->next->next = p; } Status CreatAscend(LinkList &L,int n) { //按非降序建立n个元素的线性表 int j; LinkList p,q,s; if(n<=0) { return ERROR; } InitList(L); printf("请输入%d个元素:\n",n); s = (LinkList)malloc(sizeof(struct LNode)); //第一个结点 scanf("%d",&s->data); s->next = NULL; L->next = s; for(j=1; j<n; j++) { s = (LinkList)malloc(sizeof(struct LNode)); //其余结点 scanf("%d",&s->data); q = L; p = L->next; while(p && p->data<s->data) { q = p; p = p->next; //指针后移 } s->next = q->next; //元素插在q的后面 q->next = s; } return OK; } Status CreatDescend(LinkList &L,int n) { //按非升序建立n个元素的线性表 int j; LinkList p,q,s; if(n<=0) { return ERROR; } InitList(L); printf("请输入%d个元素:\n",n); s = (LinkList)malloc(sizeof(struct LNode)); //第一个结点 scanf("%d",&s->data); s->next = NULL; L->next = s; for(j=1; j<n; j++) { s = (LinkList)malloc(sizeof(struct LNode)); //其余节点 scanf("%d",&s->data); q = L; p = L->next; while(p&&p->data>s->data) { q = p; p = p->next; //指针后移 } s->next = q->next; //元素插在q的后面 q->next = s; } return OK; }
4、应用:只展示图片,其余自己动手!
5、参考资料:
《数据结构(C语言版)》——严蔚敏
《数据结构》——高一凡
《C++ Primer Plus(第6版)》
《C程序设计》——谭浩强
6、相关文章:
Java实现:https://blog.csdn.net/weixin_43796828/article/details/95611547
标准C实现:https://blog.csdn.net/weixin_43796828/article/details/95616496
- 数据结构 带头结点的单链表 操作大全 最全的链表操作(c++实现)
- 用C++实现数据结构二 带头结点的单链表
- 经典算法与数据结构的c++实现——带头结点的单链表
- 带头结点的链表尾插法C++实现
- 【C++数据结构学习笔记---线性表】带头结点的双向循环链表
- 【c++版数据结构】之用带头节点的单链表实现一元多项式(C语言版)
- 数据结构实验-用C++实现带头结点的循环链表
- 【c++版数据结构】之循环单链表的实现(带头结点以及尾节点)
- 不带头结点的链表头插法C++实现
- 数据结构(二)——单链表 、带头结点的单链表、循环链表 及其实现
- C++实现带头结点单链表
- 数据结构(5)线性表之链表C++实现带头结点的单链表合并
- 【c++版数据结构】之循环双链表的实现(带头结点以及尾节点)
- 【c++版数据结构】之循环单链表的实现(带头结点以及尾节点)
- 【c++版数据结构】之双链表的实现(带头结点以及尾节点)
- C++模板实现双向循环链表(有带头结点)
- 带头结点的链表头插法C++实现
- 数据结构(四)——单链表 、带头结点的单链表、循环链表 及其实现
- 数据结构-java与c实现带头结点的单链表
- 【c++版数据结构】之单链表的实现(带头结点以及尾节点)