线性表的链式存储结构(C语言实现)

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 
  4 #define OK 1
  5 #define ERR 2
  6 #define TRUE 1
  7 #define FALSE 0
  8 
  9 typedef int status; //定义函数返回的状态,OK & ERR
 10 typedef char datatype; //定义线性表中每个结点的数据类型,这里暂定为字符型
 11 
 12 typedef struct LinkList_anon{
 13     datatype data; //数据区
 14     struct LinkList_anon * next; //指针区
 15 } LinkList;
 16 
 17 /* 函数原型,线性表的基本操作 */
 18 LinkList *createLinkList(datatype first_node_value);
 19 status isEmpty(LinkList *L);
 20 void clear(LinkList **L);
 21 int getLength(LinkList *L);
 22 int locateNode(LinkList *L,datatype node_to_locate);
 23 datatype getNode(LinkList *L, int index);
 24 status insert(LinkList **L, int index, datatype node_to_insert);
 25 status delete(LinkList **L, int index);
 26 void showList(LinkList *L);
 27 
 28 int main(){
 29     /* 测试 */
 30     LinkList *root; //指向线性表
 31     root=createLinkList('A'); //创建一个线性表
 32     printf("Length = %d\n",getLength(root)); //打印线性表的当前长度
 33     printf("isEmpty = %d\n",isEmpty(root)); //打印线性表是否为空
 34     insert(&root,0,'B'); //头插法插入2个结点
 35     insert(&root,0,'C');
 36     printf("Length = %d\n",getLength(root));
 37     printf("isEmpty = %d\n",isEmpty(root));
 38     showList(root);
 39     putchar('\n');
 40     insert(&root,getLength(root),'D'); //尾插法插入2个结点
 41     insert(&root,getLength(root),'E'); //尾插法插入2个结点
 42     printf("Length = %d\n",getLength(root));
 43     printf("isEmpty = %d\n",isEmpty(root));
 44     showList(root);
 45     putchar('\n');
 46     insert(&root,1,'F'); //在index=1(第2个结点)插入新结点
 47     insert(&root,2,'G'); //在index=2(第3个结点)插入新节点
 48     insert(&root,2,'H'); //在index=2(第3个结点)插入新节点
 49     printf("Length = %d\n",getLength(root));
 50     printf("isEmpty = %d\n",isEmpty(root));
 51     showList(root); //打印线性表
 52     putchar('\n');
 53     delete(&root,0); //删除头结点
 54     showList(root);
 55     putchar('\n');
 56     delete(&root,getLength(root)-1); //删除尾结点
 57     showList(root);
 58     putchar('\n');
 59     delete(&root,3); //删除index=3(第4个)结点
 60     showList(root);
 61     putchar('\n');
 62     printf("Locate = %d\n",locateNode(root,'A')); //打印查找到的结点的位置
 63     printf("getNode = %c\n",getNode(root,1)); //打印下标是1的结点的值
 64     clear(&root); //清空线性表
 65     printf("isEmpty = %d",isEmpty(root));
 66 
 67     return 0;
 68 }
 69 
 70 LinkList *createLinkList(datatype first_node_value){
 71     LinkList *tmp;
 72     tmp=malloc(sizeof(LinkList));//void*类型指针能自动转为其他类型的指针
 73     tmp->data=first_node_value; //初始化头指针的数据区
 74     tmp->next=NULL; //初始化头结点的指针区
 75     return tmp;
 76 }
 77 status isEmpty(LinkList *L){
 78     if (L==NULL)
 79         return TRUE; //链表为空返回TRUE
 80     else
 81         return FALSE; //链表不为空返回FALSE
 82 }
 83 void clear(LinkList **L){
 84     if (isEmpty(*L)==FALSE){
 85         //不为空时才执行删除
 86         LinkList * p,* q; //p始终指向当前要被删除的结点,而q始终指向要被删除的结点的下一个
 87         p=*L; //将p指向线性表的头结点
 88         while (p!=NULL){
 89             //p不是NULL就继续循环
 90             q=p->next; //q始终指向下一个结点
 91             free(p); //释放p所指的结点
 92             p=q; //交换
 93         }
 94         (*L)->next=NULL; //将头结点的指针区置空
 95         *L=NULL; //将指向线性表的指针设为NULL
 96     }
 97 }
 98 int getLength(LinkList *L){
 99     int i=0;
100     LinkList * p=L;
101     if (isEmpty(L)==TRUE) return 0;
102     while (p){
103         i++;
104         p=p->next;
105     }
106     return i;
107 }
108 int locateNode(LinkList *L, datatype node_to_locate){
109     //返回找到的结点的index
110     //node_to_locate应当是能唯一标识一个结点的数据,否则只返回匹配的第一个结点
111     int i;
112     int total=getLength(L);
113     LinkList * p=L;
114     for (i=0; i<total; i++){
115         if (p->data==node_to_locate)
116             return i;
117         else
118             p=p->next;
119     }
120     return -1; //未找到任何匹配
121 }
122 datatype getNode(LinkList *L, int index){
123     //index表示线性表中第N个结点,头结点的index是0
124     int i=0; //计数器
125     LinkList * p=L; //临时结点,用于遍历
126     if (isEmpty(L)==TRUE) return (datatype)ERR; //线性表为空
127     while (p!=NULL && i<index){
128         //p不是NULL且j还没等于i时,循环继续
129         p=p->next;
130         i++;
131     }
132     return p->data;
133 }
134 status insert(LinkList **L, int index, datatype node_to_insert){
135     //node_to_insert表示想要插入的结点
136     //当列表为空时,只有index=0才能插入
137     //当index=0时,即头插,会修改传入的指针,将其指向新的头结点
138     //当index=getLength(root)时,即尾插
139     int k;
140     LinkList * p;
141     LinkList * tmp=malloc(sizeof(LinkList));
142     if (index<0) return ERR; //index不在有效范围
143     if (index==0){
144         //头插
145         tmp->next=*L; //将新结点的指针区指向链表的头结点,随之,新节点变成链表的头结点
146         tmp->data=node_to_insert; //数据区
147         *L=tmp; //将原来指向头结点的指针修改为现在的新头结点
148         return OK;
149     }
150     if (index>getLength(*L)-1){
151         //尾插
152         tmp->next=NULL; //因为是尾插,此结点是链表的最后一个结点,所以指针区为NULL
153         tmp->data=node_to_insert; //数据区
154         p=*L; //变量p用于遍历
155         while (p){
156             //寻找当前线性表的最后一个结点,用于将新结点附在它的后面
157             if (p->next==NULL)
158                 //找到了当前链表的最后一个
159                 break;
160             p=p->next; //继续下一个结点
161         }
162         p->next=tmp; //将原来线性表的最后一个结点指向新的尾结点
163         return OK;
164     }
165     //不是头插也不是尾插
166     k=0;
167     p=*L;
168     for (k=0; k<index-1; k++){
169         //遍历到第index个结点的前一个结点,头结点的index等于0
170         //利用index的前一个结点的next就可以知道第index个结点
171         p=p->next;
172     }
173     tmp->next=p->next; //把tmp接到index前面
174     p->next=tmp; //再把tmp接到index前一个结点的后面
175     tmp->data=node_to_insert; //数据区
176     return OK;
177 }
178 status delete(LinkList **L, int index){
179     //当index=0时,即头删
180     //当index=getLength(root)-1时,即尾删
181     int k;
182     LinkList * p,* q;
183     if (index<0) return ERR; //index不在有效范围
184     if (index==0){
185         //头删
186         p=(*L)->next; //先将原来的头结点的下一个结点的指针保存起来
187         free(*L); //释放原来的头结点
188         *L=p; //将原来指向头结点的指针修改为现在的新头结点
189         return OK;
190     }
191     if (index>getLength(*L)-2){
192         //尾删
193         p=*L; //变量p用于遍历
194         while (p){
195             //寻找当前线性表的最后一个结点的前一个结点,将这个结点的后一个结点(即最后一个结点)删除
196             if (p->next->next==NULL)
197                 //找到
198                 break;
199             p=p->next; //继续下一个结点
200         }
201         free(p->next); //将原来线性表的最后一个结点删除
202         p->next=NULL; //将原来的倒数第二个结点变成尾结点
203         return OK;
204     }
205     //不是头插也不是尾插
206     p=*L;
207     for (k=0; k<index-1; k++){
208                 //printf("**********%d\n",index);
209         //遍历到第index个结点的前一个结点,头结点的index等于0
210         //利用index的前一个结点的next就可以知道第index个结点
211         p=p->next;
212     }
213     q=p->next;
214     p->next=p->next->next; //将index前一个结点和后一个结点相连
215     free(q); //删除index的结点
216     return OK;
217 
218 }
219 void showList(LinkList *L){
220     int i;
221     int total=getLength(L);
222     LinkList * p=L;
223     for (i=0; i<total; i++){
224         printf("%c\t",p->data); p=p->next;
225     }
226 }
227 
228 /*
229     链式存储结构的线性表的优缺点:(注意,上述实现的链式线性表叫做单链表)
230     优点:
231         1.插入和删除的时间复杂度是0(1)
232         2.存储空间不受预设限制
233     缺点:
234         1.寻找某个结点需要进行遍历操作
235     另外,
236         把单链表的尾结点的指针区指向头结点,便成为循环链表;
237         把单链表的每个结点再增加一个指针区,新加的指针区指向前一个结点,便成为双向链表
238 */
239 /* 环境: Code::Blocks with GCC 5.1 */

运行截图:

在编写当中,还发生过一个Bug,但是找不到原因,具体如下:

刚开始编写delete函数时,

free(q); //删除index的结点

错误的写成了

free(p->next);

结果程序一直卡在这行代码处(控制台的光标一直闪烁),我在想,即便是结点删错了,也不应该导致free函数卡住呀?而且p->next确实存在(图中的A结点)。

猜你喜欢

转载自www.cnblogs.com/ryzz/p/12215331.html