顺序表
重点
函数指针与指针函数
关于函数指针与指针函数的概念,其实很好理解。
首先,我们要知道什么是函数,什么是指针。
- 函数:emm,通俗来讲就是把一系列操作封装好,然后有入口(参数),可以有出口(返回值)。
- 指针:在C语言中,直接这么理解可以省很多事情:指针就是地址,地址就是指针。稍微解释下的话,就是指针变量用于存放内存地址。
好了,再拓展下函数与指针的知识: - 函数拓展:在面向对象语言中,有类、对象、方法等概念。类中的函数就称为方法。比如说人是一个类,那么特朗普就是个对象,特朗普吃饭就是一个方法。特朗普的身高体重就是成员变量等。
- 指针拓展:整形指针是存放整形变量的内存地址的变量,字符指针是存放字符变量的内存的地址。多级指针是存放指针变量的内存的地址。
现在来正式理解函数指针与指针函数:
-
函数指针:存放函数的内存的地址,类比面向对象语言中的方法与成员变量,当我们认为函数与整形变量具有同等的地位时(都占用内存),我们可以为函数定义一个指针。
- 注意:整形变量的指针 定义方法
int * p
是因为描述整形变量只有一个int,而描述函数有参数和返回值,所以函数指针的写法是:int (*fun)(int a,int b)
- 注意:整形变量的指针 定义方法
-
指针函数:返回值是指针变量的函数,这个就很简单了。
写法int* fun(int a,int b)
小练习:
int* (*fun)(int *a)
是啥:
答案: 这是一个参数为整形指针,返回值为整形指针的指针函数。
插入函数的实现
我们线性顺序表的插入方案效率最高只有O(n)。下面来解释下这个思路,我太懒了,所以就不写代码了。
对于顺序表,最大的特点是连续,所以我们没有办法拆开一块连续的内存放入一个数据。那么只能退而求其次:
一个顺序表模拟成十个人排队买饭
这时候第五个人我发现我认识。
我就很臭不要脸的站在了第五个人的前面
然后我后面的人都往后退了一个位置
我这个数据就被插入到顺序表了
完
删除函数的实现
接着上面的说:
有个壮汉看我插队很不爽
一脚给我踹出了队伍。
然后,我后面的每个人都向前移动了一个位置。
清空与销毁
在了解这个清空与销毁之前,先了解一个概念:内存泄露
我们模拟一个场景:
我去拿快递,很兴奋,拿到快递之后就把快递盒子扔掉了。我就走了,然后有个喜欢我的姑娘看到了这一幕,偷偷的捡起了盒子,拍下了我快递单,然后我就有女朋友了,就再也不能敲代码了,是不是很恐怖?
在这个故事中,我拿着快递盒子的手就是一个指针,快递盒子就是内存。这个指针指向内存的时候,没问题。当我不指向内存了,这内存就没用了,然后会被有心人利用,对程序造成危险。这个内存泄露在c、c++中是个很严重的问题,因为c、c++都要开发者自己把快递盒上的快递单撕个粉碎,但是java就有良好的内存管理机制,一旦快递盒被扔出去,就自动被粉碎。这个就说很多了。总之一句话,一个优秀的开发者,是不会有女朋友的(手动狗头)
为什么要介绍内存泄露呢?
其实就是我们清空与销毁的区别。
清空函数中:我们只是把pr->length归零了,内存没有变化。(其实也可以变化,只不过会影响效率,我们不变化的原因只是没有必要。为啥没有必要呢?因为我们所有的操作都先判断了下length)
销毁函数中:我们通过对elem指针执行free操作,释放掉了所有的内存。
区别:清空之后,我们还需要对顺序表进行操作。所以内存不能释放,也没有必要释放。销毁之后,我们不要这个顺序表了,所以我们把内存归还给系统。
换句话说:
- 清空==我把快递盒放下,又拿起来了。快递盒还是我的。
- 销毁==我把快递盒扔了。
引用
老师在上课的时候说的不太清楚,或者怕我弄混了吧,我来形象的记录下,反正闲的没事干。
引用这个在c语言中是没有的, 因为这个是c++的语法。注意C语言没有引用
下面来比较下:
void fun(int a)
如果程序调用这个函数,a作为形参只是接收到了实参的一个副本,也就是说,改变实参改变不了形参
void fun(int * a)
参数为指针,可以通过函数修改实参的值,只不过需要用*a来操作
void fun(int &a)
可以用a = 10;的方法修改实参的值,也就是说,可以达到指针的效果,并且比指针用起来更舒服
剩下的就是干货,直接上代码
说明
为了克制自己不自觉的使用c++简单的语法和函数,下面的代码均用纯c实现。
操作
int InitList(SortList * pr); //初始化
int InsertList(SortList* pr,int i, Student e); //插入
int DelList(SortList* pr,int i,Student *temp);//删除
int PrintList(SortList * pr);//打印
void ClearList(SortList * pr) ;//清空顺序表
void DestoryList(SortList * pr);//销毁顺序表
int LenList(SortList * pr);//获取表长
int isEmptyList(SortList * pr);//是否为空表
int getElemList(SortList * pr,int i,Student * data);//按下标找数据
int compare(Student *a,Student *b);
int LocateElem(SortList* pr,Student * e,int (*compare)(Student *a,Student*b));// 每个元素与e进行函数操作
int PreElem(SortList * pr,Student* e,Student * value);//求某元素的前驱元素
int NextElem(SortList * pr,Student* e,Student * value);//求某元素的后继元素
int solve(Student *a);
int ListSolve(SortList *pr,int (*solve)(Student *a));//元素分别执行函数
代码实现
/**
project: 线性表的顺序存储实现
author: mmciel
time:2019年3月6日23:54:54
version:1.0
update:
2019年3月6日23:56:23
实现顺序表的初始化
2019年3月7日09:50:02
实现顺序表的插入、遍历、删除
2019年3月7日01:02:06
实现顺序表关于函数指针等的操作
2019年3月8日18:21:00
实现顺序表剩下的操作
2019年3月8日20:00:43
完成调试与修改
*/
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#define LIST_INIT_SIZE 100 //初始化顺序表的大小
#define LIST_ADD 10 //每次顺序表内存扩展的大小
//定义一个学生类型,用于复杂一点的测试
typedef struct{
int number;
char name[20];
int score;
}Student;
//定义连续存储的线性表
typedef struct{
Student* elem;//学生类型的指针
int length;//表中的数据长度
int listsize; //表的当前内存容量
}SortList;
int InitList(SortList * pr); //初始化
int InsertList(SortList* pr,int i, Student e); //插入
int DelList(SortList* pr,int i,Student *temp);//删除
int PrintList(SortList * pr);//打印
void ClearList(SortList * pr) ;//清空顺序表
void DestoryList(SortList * pr);//销毁顺序表
int LenList(SortList * pr);//获取表长
int isEmptyList(SortList * pr);//是否为空表
int getElemList(SortList * pr,int i,Student * data);//按下标找数据
int compare(Student *a,Student *b);
int LocateElem(SortList* pr,Student * e,int (*compare)(Student *a,Student*b));// 每个元素与e进行函数操作
int PreElem(SortList * pr,Student* e,Student * value);//求某元素的前驱元素
int NextElem(SortList * pr,Student* e,Student * value);//求某元素的后继元素
int solve(Student *a);
int ListSolve(SortList *pr,int (*solve)(Student *a));//元素分别执行函数
void ge(){
printf("===============================================\n");
}
int main()
{
//初始化几个数据
Student studata[] = {
{1000,"xiao_zhang",99},
{1001,"xiao_zhao",80},
{1002,"xiao_liu",70}
};
Student temp_data1 = {1004,"xiao_he",100};
Student temp_data2 = {1002,"xiao_liu",70};
Student stu;
//定义一个顺序表
SortList data;
SortList * pr = &data;
//初始化
if(InitList(pr)){
printf("init success!\n");
}
else{
printf("init error!\n");
}
ge();
//插入
int ia = InsertList(pr,1,studata[0]);
int ib = InsertList(pr,1,studata[1]);
int ic = InsertList(pr,2,studata[2]);
if(ia == ib == ic){
printf("insert success\n");
}
else{
printf("insert error\n");
}
PrintList(pr);
ge();
//删除
int da = DelList(pr,1,&stu);
PrintList(pr);
ge();
//表长
printf("length = %d\n",LenList(pr));
ge();
//按照第几个找数据
getElemList(pr,1,&stu);
printf("%-10d%-10s%-10d\n",stu.number,stu.name,stu.score);
ge();
//求前驱和后继
ib = InsertList(pr,1,studata[1]);//先凑够三个
PrintList(pr);
PreElem(pr,&studata[2],&stu);
printf("前驱:%-10d%-10s%-10d\n",stu.number,stu.name,stu.score);
NextElem(pr,&studata[2],&stu);
printf("后继:%-10d%-10s%-10d\n",stu.number,stu.name,stu.score);
ge();
//与指定元素进行函数操作
printf("%d\n",LocateElem(pr,&studata[2],&compare));
ge();
// 所有元素执行指定元素
ListSolve(pr,&solve);
ge();
//清空与判空
ClearList(pr);
if(isEmptyList(pr)){
printf("empty list\n");
}
ge();
//销毁
DestoryList(pr);
if(!pr->elem){
printf("destory success\n");
}
else{
printf("destory error\n");
}
return 0;
}
//初始化指定长度的顺序表
int InitList(SortList * pr){
/*
采用动态内存分配的方案
pr: 顺序表指针,此时指针并没有大量的内存可以使用
*/
(*pr).elem = (Student*)malloc(LIST_INIT_SIZE*sizeof(Student));
if(!(*pr).elem)
exit(-1);
(*pr).length = 0;
(*pr).listsize = LIST_INIT_SIZE;
return 1;
}
//插入元素
int InsertList(SortList* pr,int i, Student e)
{
/*
在线性表pr中的第i个位置之前插入一个元素e
实现思路:先后移,然后把e存入指定位置
pr:顺序表
i:需要插入的位置
e: 需要插入的数据
*/
//检查i的合法性
if(i<1 || i>(*pr).length+1)
return 0;
//增加空间分配
if((*pr).length >= (*pr).listsize){
Student* newbase = (Student*)realloc( (*pr).elem, ((*pr).listsize+LIST_ADD)*sizeof(Student) );
if(!newbase){
exit(-1);
}
(*pr).elem = newbase;
(*pr).listsize += LIST_ADD;
//length不用更新 ,因为还没有添加
}
//取得待插入的地址
Student * temp = &((*pr).elem[i-1]);
//元素后移
//利用指针移动i后面的元素腾出位置
Student * p;
for(p = & ( (*pr).elem[(*pr).length-1] ) ;p >= temp; --p){
*(p+1) = *p;
}
*temp = e;
(*pr).length++;
return 1;
}
//删除元素
int DelList(SortList* pr,int i,Student *temp)
{
/*
在顺序表pr中,删除第i位置的元素,删除元素存入temp备份
pr:顺序表
i:待删除的位置
temp:删除后的数据备份
*/
//判断i值合法性
if(i>pr->length || i<1){
return 0;
}
//获得被删除元素的地址
Student * del = (pr->elem)+(i-1);
*temp = *del;//备份
Student * wei = (pr->elem)+(pr->length-1);//获取尾部地址
for(del++;del<=wei;del++){
*(del-1) = *del;
}
(*pr).length--;
return 1;
}
//清空
void ClearList(SortList * pr){
pr->length = 0;
}
//销毁
void DestoryList(SortList * pr){
free(pr->elem) ;
pr->elem = NULL;
pr->length = 0;
pr->listsize = 0;
}
//遍历元素
int PrintList(SortList * pr){
/*
遍历pr 打印属性
*/
int i=0;
int len = (*pr).length;
Student * p = pr->elem;//不能直接移动pr->elem 会导致真正的elem发生改变
printf("%-10s%-10s%-10s\n","number","name","score");
for(int i=0;i<len;i++){
printf("%-10d%-10s%-10d\n",p->number,p->name,p->score);
p++;//elem前进
}
return 1;
}
//获取表长
int LenList(SortList * pr){
return pr->length;
}
//是否为空表
int isEmptyList(SortList * pr){
if(pr->length == 0){
return 1;
}
else{
return 0;
}
}
int getElemList(SortList * pr,int i,Student * data){
/*
在pr顺序表中 提取下标为index 的数据到data
*/
//检查i的合法性
if(i<1 || i>pr->length){
return 0;
}
else{
*data = *((pr->elem)+(i-1));
return 1;
}
}
int compare(Student *a,Student *b){
if(!strcmp(a->name,b->name) && a->number==b->number && a->score == b->score){
return 1;
}
else{
return 0;
}
}
// 查找与e相同的元素的位置(不是下标)
int LocateElem(SortList* pr,Student * e,int (*compare)(Student *a,Student*b)){
int i=1;//代表的是长度 所以不是想象中的i=0;
Student * p=pr->elem;
while (i<=pr->length && !(*compare)(p++,e))
++i;
if (i<=pr->length)
return i;
else
return 0;
}
//求某元素的前驱元素
int PreElem(SortList * pr,Student* e,Student * value){
int i = 1;
Student *p = pr->elem;
for(;i<pr->length;i++,p++){
if(p->number == e->number && p->score == e->score && !(strcmp(p->name,e->name)) ){
*value = *(p-1);
return 1;
}
}
return 0;
}
//求某元素的后继元素
int NextElem(SortList * pr,Student* e,Student * value){
int i = 0;
Student *p = pr->elem;
for(;i<pr->length-1;i++,p++){
if(p->number == e->number && p->score == e->score && !(strcmp(p->name,e->name)) ){
*value = *(p+1);
return 1;
}
}
return 0;
}
int solve(Student *a){
Student temp = *a;
printf("%-10d%-10s%-10d\n",temp.number,temp.name,temp.score);
}
int ListSolve(SortList *pr,int (*solve)(Student *a)){
Student * e = pr->elem;
int i=0;
for(;i<pr->length;i++){
if((*solve)(e)){
e++;
}
else{
return 0;
}
}
return 1;
}