线性表的链式存储结构
线性表的实现分顺序存储结构和链式存储结构。
上一节我们学学习了线性表的实现分顺序存储结构,并实现解顺序存储的基本操作。
这一节我们来学习线性表链式存储结构,那我们再想象一下我为什么我们要引入链式存储结构,万物存在必有其道理
主要还是因为线性存储结构存在着这样一个问题:当我们需要插入和删除元素时,就必须挪动大量与之无关的元素,因为线性存储结构结点与节点之间的关系是相邻关系,一个节点挨着一个节点
如为了插入或者删除一个元素移动大量的元素,这样就降低了程序运行效率。
当我们引入
顾名思义链式存储结构,数据与数据之间是以链式关系来产生连接的的,我们可以脑部一下锁链的样子,不扯淡了,进入正题--
我们如何定义一个链式存储结构的节点呢?
/*Node表示一个节点*/ typedef struct Node{ int data; //数据域 struct Node* next; //存放下一个节点的指针 }Node; typedef struct Node* LinkList; /*取了一个别名,定义LinkList = Node*,用于存放节点的指针*/
一个节点包括一个数据域和指针域
我们将这种只带有一个指针域的线性表称为单链表。
链表中第一个结点的存储位置叫做头指针。
单链表的第一个结点前附设一个结点,称为头结点。
注意可以没有头节点,但是要是链表,就一定存在头指针。
那么问题来了,我们如何区分头节点和头指针呢?
头指针:是指向链表的指针,如果不存在头节点,那么头指针就会指向链表的第一个节点。
头节点:实际上是不存在的,只不过是为了链表的一些操作方便而设置的,头节点与第一个节点以链式关系相连,并且头节点的数据域没有意义,指针域存放第一个节点的地址。
单链表的插入
s->next =p->next;
p->next=s; //注意前后顺序不能调
单链表的删除
q= p->next;
p->next = q->next;
free(q);
单链表的建表(头插法)
顾名思义直接插在第一位,就是头节点的后面。
1 void CreateListHead(LinkList *L,int n){ 2 3 LinkList p; 4 *L = (Node*)malloc(sizeof(Node)); //生成的新节点节点要初始化 5 (*L)->next=NULL; //并指向空 6 7 for(int i=0;i<n;i++){ 8 p =(Node*)malloc(sizeof(Node)); //新生成的节点节点要初始化 9 p->data=i; 10 p->next=(*L)->next; //这里不能指向NULL 11 (*L)->next =p; //两级指针 12 } 13 }
单链表的建表(尾插法)
1 void CreateListTail(LinkList *L ,int n){ 2 3 LinkList p,r; //生成节点p,存放Node地址 4 *L = (Node*)malloc(sizeof(Node)); //生成的头节点节点要初始化 5 (*L)->next=NULL; 6 r = *L; //用于遍历 7 8 for(int i=0;i<n;i++){ 9 p =(Node*)malloc(sizeof(Node)); //新生成的节点节点要初始化 10 p->data=i; 11 r->next=p; 12 r=p; //r指针移动到p上r ,以便 13 } 14 r->next=NULL; //最后一个指向空 15 }
单链表的遍历
1 void TraverseList(LinkList L){ 2 LinkList p; 3 p = L->next; 4 while(p){ 5 printf("%d ",p->data); 6 p=p->next; 7 } 8 }
清空单链表
1 /*清空链表*/ 2 void ClearList(LinkList L){ 3 LinkList p ,q; 4 p = L->next; //指向第一个元素 5 while(p){ 6 q=p->next; //q指向了p的下一个 7 free(p); //释放内存 8 p =q; 9 } 10 L->next =NULL; //头节点指向空 11 }
其它的一下操作见下面代码吧
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include "time.h" 4 5 /*Node表示一个节点*/ 6 typedef struct Node{ 7 int data; 8 struct Node* next; 9 }Node; 10 typedef struct Node* LinkList; /*定义LinkList*/ 11 12 /*初始化链表*/ 13 int InitList(LinkList *L){ 14 *L=(LinkList)malloc(sizeof(Node)); //这里的*L就是Node节点的指针对象 15 if(!(*L)) //申请内存失败 16 return 0; 17 (*L)->next=NULL; 18 return 1; 19 } 20 21 //头插法,n表示插入的个数 22 void CreateListHead(LinkList *L,int n){ 23 24 LinkList p; 25 *L = (Node*)malloc(sizeof(Node)); //生成的新节点节点要初始化 26 (*L)->next=NULL; //并指向空 27 28 for(int i=0;i<n;i++){ 29 p =(Node*)malloc(sizeof(Node)); //新生成的节点节点要初始化 30 p->data=i; 31 p->next=(*L)->next; //这里不能指向NULL 32 (*L)->next =p; //两级指针 33 } 34 } 35 //尾插法,n表示插入的个数 36 void CreateListTail(LinkList *L ,int n){ 37 38 LinkList p,r; //生成节点p,存放Node地址 39 *L = (Node*)malloc(sizeof(Node)); //生成的头节点节点要初始化 40 (*L)->next=NULL; 41 r = *L; //用于遍历 42 43 for(int i=0;i<n;i++){ 44 p =(Node*)malloc(sizeof(Node)); //新生成的节点节点要初始化 45 p->data=i; 46 r->next=p; 47 r=p; //r指针移动到p上r ,以便 48 } 49 r->next=NULL; //最后一个指向空 50 } 51 52 /*遍历链表*/ 53 void TraverseList(LinkList L){ 54 LinkList p; 55 p = L->next; 56 while(p){ 57 printf("%d ",p->data); 58 p=p->next; 59 } 60 } 61 62 /*清空链表*/ 63 void ClearList(LinkList L){ 64 LinkList p ,q; 65 p = L->next; //指向第一个元素 66 while(p){ 67 q=p->next; //q指向了p的下一个 68 free(p); //释放内存 69 p =q; 70 } 71 L->next =NULL; //头节点指向空 72 } 73 74 /*获取链表长度*/ 75 int GetLengthList(LinkList L){ 76 LinkList p; 77 p=L->next; 78 int count=0; //计数器 79 while(p){ 80 count++; 81 p= p ->next; 82 } 83 return count; 84 85 } 86 87 /*删除元素*/ 88 void DeleteElem(LinkList L,int n){ 89 int count=0; 90 LinkList p ,q; 91 p =L; //注意这里的p不在指向第一个节点了 92 count =1; 93 while(p->next && count<n){ // 94 p =p->next; 95 ++count; 96 } 97 if(!(p->next) || count>n) 98 printf("没有找到可删除的元素"); 99 q= p->next; 100 p->next = q->next; 101 free(q); 102 } 103 104 /*插入元素 n是位置,c是数*/ 105 void InsertElemList(LinkList L,int n,int c){ 106 107 int count=0; 108 LinkList p,s; 109 p =L; //注意这里的p不在指向第一个节点了 110 count =1; 111 while(p->next && count<n){ // 112 p =p->next; 113 ++count; 114 } 115 s =(Node*)malloc(sizeof(Node)); 116 s->data=c; 117 s->next =p->next; 118 p->next=s; 119 120 } 121 122 /* 初始条件:顺序线性表L已存在 */ 123 /* 操作结果:返回L中第1个与e满足关系的数据元素的位序。 */ 124 /* 若这样的数据元素不存在,则返回值为0 */ 125 int LocateElem(LinkList L,int e){ 126 127 LinkList p; 128 p =L->next; 129 while(p){ 130 if(p->data == e) 131 return 1; 132 p =p->next; 133 } 134 return 0; 135 } 136 137 /*获取元素,n表示第几个,并返回查到的值*/ 138 int GetElem(LinkList L,int n){ 139 140 LinkList p; //生成节点p,存放Node地址 141 p = L->next; //p指向第一个元素 142 int count=1; //计数器 143 144 while(p && count<n){ //查找 145 p = p->next; 146 count++; 147 } 148 if(!p || count>n){ //没找到 149 return 0; 150 } 151 int ret = p->data; //找到 152 return ret; 153 } 154 155 /*判断链表是否为空*/ 156 int ListElmpty(LinkList L){ 157 158 if(L->next){ 159 return 0; 160 }else{ 161 return 1; 162 } 163 } 164 165 int main(){ 166 LinkList L1; //创建一个节点,用于头插法 167 LinkList L2; //创建一个节点,用于尾插法 168 169 printf("......头插法......\n"); 170 InitList(&L1); //初始化 171 CreateListHead(&L1,5); 172 TraverseList(L1); 173 printf("\n"); 174 175 printf("......尾插法......\n"); 176 InitList(&L2); //初始化 177 CreateListTail(&L2,5); 178 TraverseList(L2); 179 printf("\n"); 180 181 //获取元素的值 182 int getElem= GetElem(L2,3); 183 printf("%d \n",getElem); 184 185 //获取长度 186 int GetLength=GetLengthList(L2); 187 printf("L1链表的长度:%d",GetLength); 188 printf("\n"); 189 190 //删除L1中2号元素 191 printf("删除L1中2号元素:"); 192 DeleteElem(L1,2); 193 TraverseList(L1); 194 printf("\n"); 195 196 //在第三个位置插入11 197 printf("在第三个位置插入11元素:"); 198 InsertElemList(L1,3,11); 199 TraverseList(L1); 200 printf("\n"); 201 202 int localFind=LocateElem(L1,11); 203 printf("找到了吗: %d\nd",localFind); 204 205 //判断L1是否为空 206 int lstElempty=ListElmpty(L1); 207 printf("L1为空吗: %d\n",lstElempty); 208 //清空L1 209 ClearList(L1); 210 //在判断L1是否为空 211 lstElempty=ListElmpty(L1); 212 printf("L1为空吗: %d\n",lstElempty); 213 214 return 0; 215 }
写完插入和删除操作,我们便可以看出,链式存储结构对于插入和删除的优势是明显的,不需要进行大量的元素的移动。
当然单链表这么个优秀,也是存在缺点的
缺点就是其不便于进行查找和修改,每查找或者修改一个元素就要开始从头开始遍历 - -这么坑爹的吗 ?没错 就是这么坑爹 - -
所以当我们应用的场合不同 ,就用不同的存储结构。
原文地址:https://www.cnblogs.com/liuzeyu12a/p/10300110.html