数据结构【基础】

数据结构概述

数据结构需要从三方面考虑,第一个是逻辑结构,第二个是存储结构,第三个就是你需要实现一些什么。
在这里插入图片描述
我们一般在思考问题的时候,观察事物的内在联系,事物与事物之间可能是层级关系,或者平等…,数据有在程序中用什么存储结构来存储,使用数组,结构体还是其他的。

一、 顺序表

适用数据查找,适用数组来存储,为了方便起见,我们这次使用的是整型数组来表示一个个的数据,因为数据实时增减,需要使用一个整型变量来记录当前数据所在数组中的位置,为了更好的增删改查,创建三个文件
sqlist.c sqlist.h test.c
开始实现以下函数
首先在sqlist.h的头文件中:

typedef int date_t;
  2 #define N 128
  3 typedef struct sqlist_t
  4 {                                                                           
  5     date_t date[N];
  6     int last;
  7 }sqlist, *sqlink;

last用来存储当前数据的个数,每一次的增删都需要更新这个值

再声明需要实现的函数

9 sqlink list_creat();//空的顺序表的创建
 10 int list_clear(sqlink L);//清空数据
 11 int list_isempty(sqlink L);//判断是否为空
 12 int list_insert(sqlink L,date_t date,int pos);//按位置插入数据
 13 int list_free(sqlink* L);//释放顺序表
 14 int list_purge(sqlink L , int pos);//删除某一个位置上的元素
 15 int list_show(sqlink L);//顺序表的显示
 16 int list_merge(sqlink L1,sqlink L2);//顺序表的合并
 17 int list_find(sqlink L,date_t date);//查找数据
 18 int List_PurgeRepeat(sqlink L);//顺序表删除重复数据                         
~   

1、创建一个空的顺序表

sqlink list_creat();//空的顺序表的创建

示例:

sqlink list_creat()                                                       
 11 {                                                                         
 12     sqlink ptr = (sqlink)malloc(sizeof(sqlist));
 13     if(ptr == NULL)                                                                                                       
 14     {
 15         printf("list is error");
 16         return ptr;
 17     }
 18     memset(ptr,0,sizeof(sqlist));
 19     //初始化的时候别忘了last要初始化
 20     ptr->last = -1;
 21     return ptr; 
 22 }   

注意事项

1、malloc申请的空间没有初始化,很可能其中的数据是随机值,需要使用memset进行清零操作。也可以使用calloc来代替,用法

 void *calloc(unsigned int num, unsigned int size)
 >num:需要申请的个数,size:每一个所占字节数-----总内存:num*size

示例:

sqlink ptr = (sqlink)calloc(sizeof(sqlist)/sizeof(int) , sizeof(int));

2、前面都弄完之后,一定要记得把last置-1。

2、顺序表的插入和删除

int list_insert(sqlink L,date_t date,int pos);//按位置插入数据
int list_purge(sqlink L , int pos);//按位置删除数据
int list_insert_tail(sqlink L,date_t date)
int List_PurgeRepeat(sqlink L)//删除重复元素

示例:

列表数据的插入

int list_insert(sqlink L,date_t date,int pos)
 59 {
 60     if(L == NULL)
 61     {
 62         printf("list is error\n");
 63         return -1;
 64     }
 65     if(pos < 0||pos >L->last+1)
 66     {
 67         printf("pos is ilegal\n");
 68         return -1;
 69     }
 70     if(L->last >= N)
 71     {
 72         printf("list has been filled");
 73     }
 74     int i;
 75     for(i = L->last;i >=pos;i--)
 76     {
 77         L->date[i+1] = L->date[i];
 78     }
 79     L->date[pos] = date;
 80     L->last++;
 81     return 0;
 82 }

列表数据的删除

int list_purge(sqlink L , int pos)
119 {
120     if(L == NULL)
121     {
122         printf("list is error\n");
123         return -1;
124     }
125     if(L->last == -1 || pos < 0 || pos > L->last)
126     {
127         printf("error\n");
128         return -1;
129     }
130     int i = pos;
131     while(i <=L->last-1)//考虑到列表一开始是满的话,就不能操作last+1上的数据
132     {
133         L->date[i] = L->date[i+1];
134         i++;
135     }
136     L->date[i] = 0;
137     //更新长度
138     L->last--;
139     return 0;
140 }

列表数据的尾部插入

int list_insert_tail(sqlink L,date_t date)
168 {
169     if(L == NULL)
170     {
171         printf("%s list is ilegal\n",__FUNCTION__);
172         return -1;
173     }
174     if(L->last == N)
175     {
176         printf("%s list is full ,cannot insert",__FUNCTION__);
177         return -1;
178     }
179     int temp = L->last;
180     L->date[temp+1] = date;
181     L->last++;
182     return 0;
183 }           

列表删除重复的数据

3 int List_PurgeRepeat(sqlink L)//删除重复元素
204 {
205     if(L == NULL)
206     {
207         printf("list is error");
208         return -1;
209     }
//列表中仅有一个数据是不会重复的,我们以第一个数据为标准
//之后的数据依次和前面的数据进行比较,重复则删除
210     int i = 1,j;
211     while(i <= L->last)
212     {
213         j = 0;
214         while(j < i)
215         {
//前面的数据依次往后,j++,直到遇到重复的了,在删除,
//则继续本次循环可以提前退出了
216             if(L->date[i]==L->date[j])
217             {
218                 list_purge(L,i);
219                 break;
220             }
221             j++;
222         }   
//如果没有提前退出,也就是说while循环走完全程,这个时候,j==i;
//后面的数据需要往后移动了i++,为啥数据重复了就不用i++呢,那是
//因为使用list_purge这个函数,后面的数据删除后,不会空着这一块,而是
//后面的数据需要整体向前移动,这也就是顺序表在增删的时候麻烦之处。                                                                                                              
223         if(j == i)
224         {
225             i++;
226         }
227     }
228     return 0;
229 }

注意事项

1、在进行对任何数据的增加和删除时,都需要检查用户传入的位置是否正确,其次,数据的数量last需要实时更新。
2、在进行插入的时候,需要考虑列表是否已满。
3、在进行删除的时候,在遍历数据的时候,如果列表已满,不能访问到last+1的位置上。
4、再进行编程时需要先构思好,可以在纸上先画个图

3、数据的查找和显示

int list_find(sqlink L,date_t date)
int list_show(const sqlink L)

数据的查找

 int list_find(sqlink L,date_t date)
 89 {
 90     if(L == NULL)
 91     {
 92         printf("list is erorr");
 93         return 0;
 94     }
 95     int i = 0;
 96     while(i <= L->last)
 97     {
 98         if(L->date[i] == date)
 99         {
100             return i;
101         }//跳出循环则说明没有找到数据
102         i++;
103     }
106     printf("date: %d is not found\n",date);
107     return -1;                                                                                                       
110 }

数据的显示

int list_show(const sqlink L)
147 {
148     if(L == NULL)
149     {
150     printf("list is error");
151         return -1;
152     }
153     int i = 0;
154     while(i <= L->last)
155     {
156         printf("%4d",L->date[i]);
157         i++;
158     }
159     putchar('\n');
160     return 0;
161 }

4、列表的合并和排序

int list_merge(sqlink L1,sqlink L2)
191 {
//这里需要判断一下这两个列表是否合法,此处省略。
192     int i = 0;
193     while(i <= L2->last)//这是需要跟在后面的列表
194     {
195             if(list_find(L1,L2->date[i])==-1)//==-1就是没有重复的元素
196             {
197                 list_insert_tail(L1,L2->date[i]);
198             }
199         i++;
200     }
201     return 0;
202 }
这个合并使用的是∪合并,也就是A = A∪B,如果B中的数据,A没有,则放到A中
,相反,则不放。

5、列表的释放和显示

int list_show(const sqlink L)
int list_free(sqlink* L)

列表的显示

int list_show(const sqlink L)
147 {
148     if(L == NULL)
149     {
150     printf("list is error");
151         return -1;
152     }
153     int i = 0;
154     while(i <= L->last)
155     {
156         printf("%4d",L->date[i]);
157         i++;
158     }
159     putchar('\n');                                                                                                        
160     return 0;
161 }

列表的释放

int list_free(sqlink* L)
113 {
114     free(*L);
115     *L = NULL;
116     return 0;
117 }

注意事项

1、在释放内存时,很容易造成悬空指针,我们发现,当我们将指针P指向的空间释放掉后,指针保存的地址还是存在的,这就造成了悬空指针,这里也是我们使用地址的地址来操作,目的就是将我们在主函数新建的指针所指向的内存释放掉,同时置NULL;
当然也可以返回NULL,用指针来接收也可以达到目的。

二、链表

链表和顺序表都是一种线性表,他的优点是不需要连续的存储空间,而且增删都比较方便,不用大动干戈的整体移动。但是缺点是查找就没有顺序表那么便捷了。
解释一下,我们为什么使用动态申请的方式来创建这些顺序表还是链表,都是因为这些空间资源是可以自己定义什么时候释放,它一直在那,总不能在一个函数中创建完,函数调用结束就销毁吧(栈)。

引入链表的定义:

  1 #include <stdio.h>                                    
  2 typedef int date_t;                                   
  3 typedef struct link_t                                 
  4 {
  5     date_t date;                                      
  6     struct link_t*node_next;                                                                                                                          
  7 }link_node,*link_p;  

因为空间不是连续的,不能用下标来找到某一个节点,顺表那一套在这就不适用了。

链表相关函数的声明

     
  9 link_p list_creat();                                  
 10 int list_tail_insert(link_p H, date_t value);//head   
 11 link_p list_get(link_p H, int pos);
 12 int list_insert(link_p H, date_t value, int pos);     
 13 int list_show(link_p H);                              
 14 int list_merge(link_p H1,link_p H2);
 15 link_p list_adimax(link_p H1,date_t*date);            
 16 int list_sort(link_p H);

1、创建链表

 link_p list_creat()
 10 {
 11     link_p H = (link_p)malloc(sizeof(link_node));
 12     if(H == NULL )
 13     {
 14         printf("get heal failed\n");
 15         return NULL;
 16     }
 17     //初始化其中的数据,date可以随意,node_next为NULL;
 18     H->node_next = NULL;
 19     H->date = 0;
 20     return H;
 21 }

返回值:头节点。

链表的插入

int list_tail_insert(link_p H, date_t value);//头部插入
int list_insert(link_p H, date_t value, int pos);

思路:1、检测参数是否正确(插入点是否合理,链表是否合理,不需要判断满)
2、创建一个节点,使用形参初始化date,next指针置空,不要引入野指针。
3、找到待插入点,再保存待插入点下一个节点的地址
4、待插入点的next指向新节点,新节点的next指向刚刚保存的地址。
示例:

 int list_tail_insert(link_p H, date_t value)
 23 {
 24     link_p P_New;                                                                                                                                     
 25     if(H == NULL)
 26     {
 27         printf("%s --space is error\n",__FUNCTION__);
 28         return -1;
 29     }
 30     //创建一个节点
 31     if(( P_New = (link_p)malloc(sizeof(link_node)))==NULL)
 32     {
 33         printf("%s--malloc failed\n",__FUNCTION__);
 34         return -1;
 35     }
 36     link_p P = H;//保存头地址,P移动找最后一个节点
 37     while(P->node_next!=NULL)
 38     {
 39         P = P->node_next;
 40     }
 41     P->node_next = P_New;
 42     //给新节点赋值
 43     P_New->date = value;
 44     P_New->node_next = NULL;
 45 
 46     return 0;
 47 }

链表的删除和清空

思路1
1、先用P_old保存需要删除的节点
2、找到要删除节点位置的前一个节点P_last(这个P_last需要判断一下是不是NULL或者已经是最后一个节点,是的话就不能删除下一个哦)
3、P_last的next = P_old的next
4、释放P_old
思路2
1、先找到需要删除的节点,保存需要删除的节点的下一个地址作为P_old;
2、把P_old的数据(date和next)拷贝给需要删除的节点
3、释放P_old

链表的排序(从小到大)

思路1:
1、将除了头节点的第一个节点 和 其他节点断开,形成两个链表。
2、后链表的每一个节点和前链表的节点依次进行比较,如果出现后面的节点比前面某一个要小,就插到这个节点的前面(这个貌似需要保存前一个节点的地址)可以使用p->node_next来比较,这样既可以比较,也可以保存p;
示例:

 int list_sort(link_p H)                                                         
321 {                                                                               
322     link_p temp_back;                                                           
323     link_p temp;                                                                
324     if(H == NULL)                                                               
325     {                                                                           
326         printf("link is error\n");                                              
327         return -1;                                                              
328     } //空链表和仅有一个节点就不需要排序了                                                                          
329     if(H->node_next == NULL || H->node_next->node_next== NULL)                  
330     {                                                                           
331         return 0;                                                               
332     }                                                                           
333     //首先保存后链表的地址,P_Back也是用来遍历后面链表的                                                      
334     link_p P_Back = H->node_next->node_next;                                    
335     //然后置空实现断开                                                          
336     H->node_next->node_next = NULL;                                             
337     link_p P = H;//用来遍历链表                                                 
338     while(P_Back != NULL)                                                       
339     {                                                                           
340         //初始P指针,因为后面的操作为改变P的指向                              
341         P = H;                                                                  
342     //P_Back和P遍历的节点进行比较                                               
343         while(P->node_next != NULL && P->node_next->date < P_Back->date)        
344         {                                                                       
345             P = P->node_next;                                                   
346         }                                                                       
347         temp_back = P_Back;                                                     
348         //首先保存需要插入的节点,然后遍历节点P_Back可以往后移动了             
349         P_Back = P_Back->node_next;                                                                                                                                                  
350         //插入之前先保存下一个地址                                              
351         temp = P->node_next;                                                    
352         P->node_next = temp_back;                                               
353         temp_back->node_next = temp;                                            
354     }                                                                           
355 
356 }

思路2:
可以将链表先保存到数组中,然后使用快速排序,然后再将数组的数据依次放会链表中。

链表的反转

思路1:
也是使用上面的插入的方法,先断开,再插入,但这次插入是头部插入。
示例:

 int link_reverse(link_p H)
358 {
359     //参数的检测忽略
360     //....
361     link_p temp,temp_Head;
362     //先初始化一下
363     temp_Head = H->node_next;
364     //断开
365     link_p P_Back;
366     P_Back = H->node_next->node_next;
367     H->node_next->node_next = NULL;                                                                                    
368     while(P_Back != NULL)
369     {
370         temp = P_Back;
371         P_Back = P_Back->node_next;
372         H->node_next = temp;
373         temp->node_next = temp_Head;
374         //上面的操作修改了temp_Head,P_Back的插入使得H不再指向temp_Head
375         temp_Head = H->node_next;
376     }
		return 0;
377 }

思路2:
一个指针 i 指向第一个节点,另一个指针j 指向最后一个节点。然后依次交换数据,可以整个节点交换当然也行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值