【辉哥带我学数据结构】线性表(一)由顺序存储实现的顺序表

顺序表

重点

函数指针与指针函数

关于函数指针与指针函数的概念,其实很好理解。
首先,我们要知道什么是函数,什么是指针。

  • 函数: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;
}


猜你喜欢

转载自blog.csdn.net/F_zmmfs/article/details/88358258