数据结构定义:研究数据存储问题;数据结构=个体的存储+个体关系的存储
算法定义:解题的方法和步骤,研究数据操作问题
算法特性
时间复杂度:大概程序要执行的次数,而非执行的时间
空间复杂度:算法执行过程中大概所占用的最大内存
可读性
健壮性
数据结构和算法的关系
程序(Program)=数据结构(Data Structure)+算法(Algorithm)
#include
struct Student
{
int sid;
char name[200];
int age;
}; //208个字节,在传值时注意内存消耗问题
void InitStruct(struct Student * pst)
{
// pst是指针变量,用(->访问结构体变量)。(*pst) 是指针所指向的变量(用 . )
(*pst).sid = 99;
strcpy(pst->name, "ILOVEYOU");
pst->age = 22;
}
void PrintStruct(struct Student st)
{
// 传入结构体变量即可,但是这种方法消耗内存,不推荐
//因为传入变量名之后需要在内存中复制此结构体
printf("%d %s %d\n",st.sid,st.name,st.age);
}
void PrintStruct2(struct Student * st)
{
// 传入结构体变量地址,节省内存
printf("%d %s %d\n",st->sid,st->name,st->age);
}
int main(void)
{
struct Student st;
InitStruct(&st);
PrintStruct(st);
PrintStruct2(st);
return 0;
}
输出结果为:
99 ILOVEYOU 22
99 ILOVEYOU 22
概念讲解2:动态内存分配问题
malloc()函数返回分配内存的首地址,故需要赋值给一个指针。
struct Student * p = (struct Student *)malloc(sizeof(struct Student));
free(p);
概念讲解3:跨函数使用内存的问题:
在函数CreateStudent(void)中分配了一段内存用来存储结构体变量,并给结构体成员分配了值。而在另一个函数PrintStruct(struct Student *pst)中传入该结构体的地址,这个函数并没有重新分配一段内存来复制 p,而是直接访问该地址。
#include
#include
struct Student
{
int sid;
int age;
};
// 指针作为函数返回值
// 此函数名称为CreateStudent(),返回值类型为Struct Student类的地址
struct Student * CreateStudent(void)
{
struct Student * p = (struct Student *)malloc(sizeof(struct Student));
p->age=99;
p->sid=40;
return p;
//指针就是地址,地址就是指针,这里返回的就是一个地址
}
void PrintStruct(struct Student *pst)
{
printf("%d %d\n",pst->sid,pst->age);
}
int main(void)
{
struct Student * ps;
ps = CreateStudent();
PrintStruct(ps);
return 0;
}
输出结果为:40,99
一句话:指针就是地址,地址就是指针
void function(int * p);
在主函数中调用方式如下,function()中传入的是变量地址:
void main()
{
int i;
function(&i); //传入的是 i 的地址,对应函数定义中的(int *)
}
#include
void Show_Array(int * p, int len)
{
int i;
p[0] = -1; //p[0]==*p p[2]=*(p+2) == a[2] == *(a+2)
//也就是p[i] == a[i]
for(i=0;i<len;i++)
printf("%d\n",p[i]);
}
int main(void)
{
int a[5] = {1,2,3,4,5};
Show_Array(a,5); //a等价&a[0],&a[0]本身就是int * 类型
printf("%d\n",a[2]);
}
void main()
{
int *p;
func(&p); //形参传入的是指针的地址,用来修改指针指向。
}
void func(int **q)
{
*q = (int *)malloc(sizeof(int)*4);
// *q == p
}
以下是线性结构-顺序存储方式[数组],操作有初始化、追加、插入、删除、数组满,空检测、排序、反转和显示。建议看过代码之后自己多试着完整地写几遍。我看完《大话数据结构》之后就感觉这个挺简单的。
#include
#include
#include
#define MAXSIZE 10
#define True 1
#define False 0
struct Arr
{
int * pBase; //存储的是第一个元素的地址
int len; //数组的长度
int cnt; //当前数组元素的有效元素数量
};
void init_arr(struct Arr * pArr, int length);
int append_arr(struct Arr * pArr, int value); //追加
int insert_arr(struct Arr * pArr, int pos, int value);
int delete_arr(struct Arr * pArr, int pos, int * pvalue);
int get(struct Arr * pArr, int pos, int * pvalue);
int is_empty(struct Arr * pArr);
int is_full(struct Arr * pArr);
void sort_arr(struct Arr * pArr);
void show_arr(struct Arr * pArr);
void inversion_arr(struct Arr * pArr);
int main(void)
{
struct Arr arr;
int val;
init_arr(&arr, MAXSIZE);
append_arr(&arr, 3);
append_arr(&arr, 1);
append_arr(&arr, -1);
append_arr(&arr, 66);
append_arr(&arr, 7);
append_arr(&arr, 5);
append_arr(&arr, 8);
append_arr(&arr, 11);
append_arr(&arr, 6);
insert_arr(&arr, 5, 100);
show_arr(&arr); //这里注意形参
delete_arr(&arr, 6, &val);
printf("删除的值为:%d\n", val);
show_arr(&arr);
inversion_arr(&arr);
show_arr(&arr);
sort_arr(&arr);
show_arr(&arr);
get(&arr, 6, &val);
printf("查找的值为:%d\n", val);
show_arr(&arr);
return 0;
}
void init_arr(struct Arr * pArr, int length)
{
//传过来的是arr的地址值,那么此时*pArr就是arr (*pArr).len = 100;
pArr->pBase = (int *)malloc(sizeof(int)*length);
if(NULL == pArr->pBase)
{
printf("动态内存分配失败!\n");
exit(-1); //终止整个程序
}
else
{
pArr->cnt = 0;
pArr->len= length;
}
return;
}
int is_empty(struct Arr * pArr)
{
if(0 == pArr->cnt)
return True;
else
return False;
}
int is_full(struct Arr * pArr)
{
if(pArr->len == pArr->cnt)
return True;
else
return False;
}
void show_arr(struct Arr * pArr)
{
int i=0;
if( is_empty(pArr) )
{
printf("当前数组为空!\n");
}
else
{
for(i=0; i<pArr->cnt; i++)
printf("%d ",pArr->pBase[i]);
printf("\n");
}
}
int append_arr(struct Arr * pArr, int value)
{
if( is_full(pArr) )
{
printf("追加失败,数组已满!\n");
return False;
}
pArr->pBase[pArr->cnt] = value;
pArr->cnt++;
return True;
}
int insert_arr(struct Arr * pArr, int pos, int value)
{ // pos 指的是第几个位置,不代表数组下表
// 这里要注意位置和下表之间的关系;
int i; //用来指示数组下标
if( is_full(&pArr) )
{
printf("插入失败,数组已满!\n");
return False;
}
if(pos<1 || pos > pArr->cnt+1) //pos>pArr->cnt+1:在pos<=pArr->cnt时,正常插入。在pos=pArr->cnt+1时,是插入到当前数组的末尾
{
printf("插入失败,位置溢出!\n");
return False;
}
for(i=pArr->cnt-1;i>=pos-1;i--)
{
pArr->pBase[i+1]=pArr->pBase[i];
}
pArr->pBase[pos-1]=value;
pArr->cnt++;
return True;
}
int delete_arr(struct Arr * pArr, int pos, int * pvalue)
{
//pos 指的是位置,i 指的是下表
int i;
if( is_empty(pArr) )
{
printf("插入失败,数组为空!\n");
return False;
}
if(pos<1 || pos > pArr->cnt)
{
printf("删除失败,位置溢出!\n");
return False;
}
*pvalue = pArr->pBase[pos-1];
for(i=pos; i<pArr->cnt;i++) // for(i=pos; i<=pArr->cnt-1;i++)
{
pArr->pBase[i-1]=pArr->pBase[i];
}
pArr->cnt--;
}
void inversion_arr(struct Arr * pArr)
{
int i = 0;
int j = pArr->cnt-1;//下标
int t;
while(i<j)
{
t = pArr->pBase[i];
pArr->pBase[i] = pArr->pBase[j];
pArr->pBase[j] = t;
i++;j--;
}
}
void sort_arr(struct Arr * pArr)
{
int i,j,t;
for(i=0; i<pArr->cnt; i++)
for(j=i+1; j<pArr->cnt; j++)
{
if(pArr->pBase[i]>pArr->pBase[j]) //冒泡排序:大数挪到后面,升序排列
{
t = pArr->pBase[i];
pArr->pBase[i]=pArr->pBase[j];
pArr->pBase[j]=t;
}
}
}
int get(struct Arr * pArr, int pos, int *pvalue)
{
if(is_empty(pArr))
{
printf("查找失败,数组为空!\n");
}
if(pos<1 || pos > pArr->cnt)
{
printf("删除失败,位置溢出!\n");
return False;
}
*pvalue = pArr->pBase[pos-1];
}
这里主要就is_empty()函数和is_full()函数调用形参时进行说明。因为两函数情况相同,这里以is_full()为例。
// 函数定义:
int is_full(struct Arr * pArr)
{
if(pArr->len == pArr->cnt)
return True;
else
return False;
}
// 函数调用:
int append_arr(struct Arr * pArr, int value)
{
if( is_full(pArr) )
{
printf("追加失败,数组已满!\n");
return False;
}
pArr->pBase[pArr->cnt] = value;
pArr->cnt++;
return True;
}
is_full(struct Arr * pArr) 的形参名称为 pArr,类型为struct Arr * (是结构体变量类型的地址)。
append_arr(struct Arr * pArr, int value)的形参名称为pArr,类型为struct Arr * (是结构体变量类型的地址)。
if( is_full(pArr) )这句话在调用is_full(struct Arr * pArr) 时用if( is_full(pArr) ),而不是if( is_full(&pArr) )的原因是:
append_arr(&arr, 3)传入的形参是结构体变量arr的地址,而is_full(struct Arr * pArr) 的形参也为结构体变量的地址,pArr就代表了&arr(即arr的地址), 所以可以直接把pArr传给is_dull();