数据结构
定义
我们把现实中大量儿复杂的问题以特定的数据类型和特定的存储结构保存到主存储器(内存)中,以及在此基础上为实现某个功能(如查找某个元素,删除某个元素,对所有元素进行排序)而执行的相应的操作,这个相应的操作也叫作算法
数据结构=个体+个体的关系
算法=对存储数据的操作
算法
衡量算法的标准
1,时间复杂度
大概程序要执行的次数,而非执行时间
2,空间复杂度
算法执行过程中大概所占用的最大内存
3,难易程度
4,健壮性
算法
狭义的算法是与数据存储方式密切相关
广义的算法是与数据的存储方式无关
泛型:
利用某种技术达到的效果就是:不同的存储方式,执行的操作是一样的
#include <stdio.h>
int main(void){
int *p; //p是个指针变量,int *表示该p变量只能存储int类型变量的地址,(只能存储int类型变量的地址)
int i=10;
int j;
p=&i;
//j=*p;
//p=10 error
//
printf("%d\n",p)
//%d 数字
//%p 地址
return 0;
}
#include <stdio.h>
void f(int* i){
*i=100;
}
void array(){
int a[5]={1,2,3,4,5};
*(a+3)==a[3]==3[a];
printf("%d\n",*(a+3));
}
void array2(){
int a[5]={1,2,3,4,5};
printf("%p\n",a+1);
printf("%p\n",a+2);
printf("%p\n",a+3);
printf("%p\n",*a+3);//等价于a[0]+3=4
printf("%d\n",*a);//int类型占4个字节,a代表数组的第一位a[0]的地址 *a就是a[0]的值
/*
0019FEC8
0019FECC
0019FED0
4
1
*/
}
void Show_Array(int *p,int len){
// p[0]=-1; //p[0]==*p p[2]==*(p+2)==*(a+2)==a[2]
for(int i=0;i<len;i++){
printf("%d\n",p[i]);
printf("%d\n",*(p+i)); //p+i的值是p+i * (p所指向的变量所占的字节数) p+i*4 int 占4个字节
}
}
int main(void){
int i=9;
f(&i);
printf("%d\n",i);
array();
array2();
int a[5]={1,2,3,4,5};
Show_Array(a,5); //a=[0]的地址=&a[0]
printf("%d\n",a[0]);
return 0;
}
--------------------------------------------------------
**p 伪代码解释
mian{
int i=10;
int *p;
p=&i;
fun(&p);
}
fun(int **p){
*p=0xFFFFFF;
表示真实指针变量的地址 上面*p的地址
}
**p 表示指针类型的指针
指针
定义
地址
内存d
单元的编号
从0开始的非负整数
范围:0–FFFFFFFF【0《------》4G-1】
指针:
指针就是地址 地址就是指针
指针变量是存放内存单元地址的变量
指针的本质是一个操作受限的非负整数
分类:
结构体
结构体变量不能加减乘除,但可以相互赋值
#include <stdio.h>
#include <string.h>
struct Student{
int sid;
char name[20];
int age;
}
int mian(void){
struct Student st={1000,"zhangsan",20};
//st.sid=99 第二种方式
//st.strcpy(st.name,"lishi");
printf("%d %s %d\n",st.sid,st.name,st.age);
//struct Student* pst=&st;
struct Student* pst;
pst=&st;
pst->sid=99; //pst->sid 等价于 (*pst).sid 而(*pst).sid等价于st.sid,所以pst->sid等价于pst->sid
}
#include <stdio.h>
#include <string.h>
struct Student{
int sid;
char name[20];
int age;
}
void fun(struct Student *stu);
void show();
int main(void){
struct Student stu; //已经为stu分配好了地址
fun(&stu);
show(&stu);
//printf("%d %s %d\n",stu.sid,stu.name,stu.age)
return 0;
}
void show(struct Student *stu){ //如果直接传stu则需要在内存中开辟一个student浪费空间和时间,直接传引用
printf("%d %s %d\n",stu->sid,stu->name,stu->age);
}
void fun(struct Student *stu){
(*stu).sid=99;
strcpy(stu->name,"zhangsan");
stu->age=22;
}
动态内存分配,必须通过手动释放
例子:int *pArr=(int *)malloc(4)
free(pArr);//把pArr代表的动态分配的内存释放
#include <stdio.h>
#include <malloc.h>
int main(int){
int a[5]={4,10,2,8,6};
int len;
printf("请输入你需要分配的数组的长度:len=");
scanf("%d",&len);
int *pArr=(int *)malloc(sizeof(int)*len); //强制转为(int*)告诉系统这是一个int类型的数组,占四个字节,malloc:动态分配,sizeof(int) 计算int类型所占字节数 *len表示分配lei个数
*pArr=4;//类似于a[0]=4
pArr[1]=10;//类似于a[1]=10
printf("%d %d\n",*pArr,pArr[1]);
free(pArr);//把pArr代表的动态分配的内存释放
return 0;
}
跨方法调用
#include <stdio.h>
#include<malloc.h>
struct Student * CreateStudent(void);
void ShowStudent(struct Student *);
struct Student{
int sid;
int age;
};
int main(void){
struct Student* ps;
ps=CreateStudent();
ShowStudent(ps);
return 0;
}
void ShowStudent(struct Student *ps){
printf("%d %d\n",ps->sid,ps->age);
}
struct Student * CreateStudent(void){
struct Student* p=(struct Student *)malloc(sizeof(struct Student));
p->sid=99;
p->age=18;
return p;
}
#include <stdio.h>
#include <malloc.h> //包含了malloc函数
#include <stdlib.h> //包含了exit函数
//定义了一个数据类型,该数据类型的名字叫struct Array,改数据类型包含三个成员
struct Array{
int* pBase; //存储的是数组第一个元素的地址
int len; //数组所能容纳的最大元素个数
int cnt; //当前数组有效元素的个数
//int increment; //自动增长因子
};
void init_array(struct Array* pArr,int length);
bool append_array(struct Array* pArr,int value);//追加
bool insert_array(struct Array* pArr,int pos,int val);//指定位置处插入,pos的值从1开始
bool delete_array(struct Array* pArr,int pos,int* pVal);
bool get(struct Array* pArr,int i,int *val);
bool is_Empty(struct Array* pArr);
bool is_full(struct Array arr); //数组是否满了
void sort_array(struct Array* pArr);
void show_array(struct Array* pArr);
void inversion_arr(struct Array* pArr);//反转
bool update_arr(struct Array* pArr,int place,int val);
int main(void){
struct Array arr;
int val;
init_array(&arr,6);
show_array(&arr);
append_array(&arr,10);
append_array(&arr,-2);
append_array(&arr,5);
append_array(&arr,3);
append_array(&arr,4);
insert_array(&arr,6,99);
if(delete_array(&arr,1,&val))
printf("删除成功,您删除的元素是:%d\n",val);
else
printf("删除失败!");
inversion_arr(&arr);
printf("反转之后的内容是:\n");
show_array(&arr);
printf("排序之后的内容是:\n");
sort_array(&arr);
show_array(&arr);
if(get(&arr,2,&val))
printf("查询到元素的值是:%d\n",val);
else
printf("为查询到有效值");
update_arr(&arr,2,99);
show_array(&arr);
return 0;
}
//初始化
void init_array(struct Array* pArr,int length){
pArr->pBase=(int *)malloc(sizeof(int) * length);
if(NULL==pArr->pBase){
//如果内存满后会分配一个NULL
printf("动态内存分配失败");
exit(-1); //终止整个程序
}else{
pArr->cnt=0;
pArr->len=length;
}
return;
}
bool is_Empty(struct Array* pArr){
if(0==pArr->len){
return true;
}else{
return false;
}
}
void show_array(struct Array* pArr){
if(is_Empty(pArr)){
printf("数组为空\n");
}else{
for(int i=0;i<pArr->cnt;i++){
printf("%d ",pArr->pBase[i]);
}
printf("\n");
}
}
bool is_full(struct Array* pArr){
if(pArr->cnt==pArr->len){
return true;
}else{
return false;
}
}
bool append_array(struct Array* pArr,int value){
//如果数组满了
if(is_full(pArr)){
return false;
}else{
//不满时追加
(pArr->pBase)[pArr->cnt]=value;
(pArr->cnt)++;
return true;
}
}
bool insert_array(struct Array* pArr,int pos,int val){
if(is_full(pArr))
return false;
if(pos<1||pos>pArr->cnt+1)
return false;
int i=0;
for(i=pArr->cnt-1;i>=pos-1;i--){
pArr->pBase[i+1]=pArr->pBase[i];
}
pArr->pBase[pos-1]=val;
pArr->cnt++;
return true;
}
bool delete_array(struct Array* pArr,int pos,int* pVal){
if(is_Empty(pArr))
return false;
if(pos<1||pos>pArr->cnt)
return false;
*pVal=pArr->pBase[pos-1];
for(int i=pos;i<pArr->cnt;i++){
pArr->pBase[i-1]=pArr->pBase[i];
}
pArr->cnt--;
return true;
}
//反转
void inversion_arr(struct Array* pArr){
int i=pArr->cnt-1;
int j=0;
int flag=0;
while(i>j){
flag=pArr->pBase[i];
pArr->pBase[i]=pArr->pBase[j];
pArr->pBase[j]=flag;
i--;
j++;
}
return;
}
void sort_array(struct Array* 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;
}
}
}
}
bool get(struct Array* pArr,int i,int *val){
if(is_Empty(pArr))
return false;
if(i<0||i>pArr->cnt)
return false;
*val=pArr->pBase[i-1];
return true;
}
bool update_arr(struct Array* pArr,int place,int val){
if(is_Empty(pArr))
return false;
if(place<0||place>pArr->cnt)
return false;
pArr->pBase[place-1]=val;
return true;
}
链表
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
typedef struct Node{
int data;
struct Node* pNext;
}NODE,*PNODE;
//NODE 等价于struct Node
//*PNODE 等价于struct Node*
PNODE create_list(void);
void traverse_list(PNODE pHead);
bool is_empty(PNODE pHead);
int length_list(PNODE);
bool insert_list(PNODE,int,int);
bool delete_list(PNODE,int,int *);//将删除的数据返回
void sort_list(PNODE);
int main(void){
PNODE pHead=NULL; //等价于 struct Node * pHead=NULL
pHead=create_list(); //create_list()功能,创建一个非循环单链表
traverse_list(pHead);
if(is_empty(pHead)){
printf("链表为空!\n");
}else{
printf("链表不为空!\n");
}
int len=length_list(pHead);
printf("链表长度是:%d\n",len);
sort_list(pHead);
printf("排序之后的长度是:");
traverse_list(pHead);
insert_list(pHead,2,99);
traverse_list(pHead);
int val;
if(delete_list(pHead,4,&val))
printf("删除成功您删除的元素是:%d\n",val);
else
printf("删除失败!\n");
traverse_list(pHead);
return 0;
}
PNODE create_list(void){
int len;//用来存放节点的有效个数
int i;
int val;//用来临时存放用户输入的节点的值
//分配了一个不存放数据的头节点
PNODE pHead=(PNODE)malloc(sizeof(NODE));
if(NULL==pHead){
printf("分配失败,程序终止\n");
exit(-1);
}
//创建一个指针永远指向尾节点 初始pTail和pHead指向同一个(空节点)
PNODE pTail=pHead; //
pTail->pNext=NULL;
printf("请输入您需要生成的节点的有效个数:len= ");
scanf("%d",&len);
for(i=0;i<len;i++){
printf("请输入第%d个节点的值:",i+1);
scanf("%d",&val);
PNODE pNew =(PNODE)malloc(sizeof(NODE));
if(NULL==pNew){
printf("分配失败,程序终止\n");
exit(-1);
}
pNew->data=val;
pTail->pNext=pNew;
pNew->pNext=NULL;
pTail=pNew; //再将pTail指向尾节点
}
return pHead;
}
void traverse_list(PNODE pHead){
PNODE p=pHead->pNext;
while(NULL!=p){
printf("%d ",p->data);
p=p->pNext;
}
printf("\n");
return;
}
bool is_empty(PNODE pHead){
if(NULL==pHead->pNext)
return true;
else
return false;
}
int length_list(PNODE pHead){
int len=0;
PNODE p=pHead->pNext;
while(NULL!=p){
len++;
p=p->pNext;
}
return len;
}
void sort_list(PNODE pHead){
int i,j,t;
int len=length_list(pHead);
PNODE p,q;
for(i=0,p=pHead->pNext;i<len-1;i++,p=p->pNext){
for(j=i+1,q=p->pNext;j<len;j++,q=q->pNext){
if(q->data<p->data){
t=q->data;
q->data=p->data;
p->data=t;
}
}
}
}
//在pHead所指向第pos个节点的前面插入一个新节点,该节点的值是val;pos从第一个开始
bool insert_list(PNODE pHead,int pos,int val){
int i=0;
PNODE p=pHead;
while(NULL!=p&&i<pos-1){
p=p->pNext;
++i;
}
// printf("i为:%d\n",i);
if(i>pos-1||NULL==p)
return false;
PNODE pNew=(PNODE)malloc(sizeof(Node));
if(NULL==pNew){
printf("动态内存分配失败!\n");
exit(-1);
}
pNew->data=val;
pNew->pNext=p->pNext;
p->pNext=pNew;
return true;
}
bool delete_list(PNODE pHead,int pos,int *pVal){
int i=0;
PNODE p=pHead;
while(NULL!=p->pNext&&i<pos-1){
p=p->pNext;
++i;
}
// printf("i为:%d\n",i);
if(i>pos-1||NULL==p->pNext)
return false;
PNODE q=p->pNext;
*pVal=q->data;
p->pNext=p->pNext->pNext;
free(q);
q=NULL;
return true;
}
复习
数据结构
狭义:
数据结构是专门研究数据存储的问题
数据的存储包含两方面:个体的存储+个体关系的存储
广义
数据结构既包含数据的存储也包含数据的操作
对存储数据的操作就是算法
算法
狭义:
算法是和数据的存储方式密切相关
广义:
算法的数据存储方式无关
这就是泛型的思想
数据的存储结构
线性
连续存储【数组】
优点:存取(查找)速度快
缺点:增删元素慢,空间通常是有限制
离散存储【链表】
优点:空间没有限制,插入删除元素的速度快
缺点:
线性结构的应用 --》 栈
线性结构的应用 --》 队列
非线性
树
图
线性结构的两种常见结构之一 栈
定义
一种可以实现“先进后出”的存储结构
分类
静态栈(数组)
动态栈(链表)
算法
出栈
压栈
应用
函数的调用
中断
表达式求值
内存分配
缓存处理
迷宫
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
typedef struct Node{
int data;
struct Node* pNext;
}NODE, * PNODE;
typedef struct Stack{
PNODE pTop; //指向顶部的指针
PNODE pBottom; //指向底部的指针
}STACK,* PSTACK;
void init(PSTACK);
void push(PSTACK,int);
void traverse(PSTACK);
bool pop(PSTACK,int*);
bool empty(PSTACK ps);
void clear(PSTACK ps);
int main(void){
STACK s;
int val;
init(&s);
push(&s,1);
push(&s,2);
push(&s,3);
push(&s,4);
push(&s,5);
traverse(&s);
/*if(pop(&s,&val))
printf("出栈成功,出栈的元素是:%d\n",val);
else
printf("出栈失败!\n");
traverse(&s);
*/
clear(&s);
traverse(&s);
return 0;
}
void init(PSTACK ps){
//pTop和pBottom都指向一个空节点
ps->pTop=(PNODE)malloc(sizeof(NODE));
if(NULL==ps->pTop){
printf("动态内存分配失败!");
exit(-1);
}else{
ps->pBottom=ps->pTop;
ps->pBottom->pNext=NULL; //清楚空节点的垃圾值
}
}
void push(PSTACK ps,int val){
PNODE pNew= (PNODE)malloc(sizeof(NODE));
pNew->data=val;
pNew->pNext=ps->pTop;
ps->pTop=pNew;
return;
}
void traverse(PSTACK ps){
PNODE p=ps->pTop; //添加一个指针指向栈顶 (因为遍历不能改变栈,所以需要复制一份指向栈顶)
while(p!=ps->pBottom){ //只要栈顶不等于栈底就一直循环
printf("%d ",p->data);
p=p->pNext;
}
printf("\n");
return;
}
bool empty(PSTACK ps){
if(ps->pBottom==ps->pTop)
return true;
else
return false;
}
//把ps所指向的栈出栈一次,并把栈的元素存入pVal形参所指向的变量中,如果出栈失败,返回false,否则返回true
bool pop(PSTACK ps,int* pVal){
if(empty(ps))
return false;
else{
PNODE r=ps->pTop;//为了清楚无用的内存
*pVal=r->data;
ps->pTop=r->pNext;
free(r);
r=NULL;
return true;
}
}
void clear(PSTACK ps){
if(empty(ps)){
return;
}
else{
PNODE p=ps->pTop; //两个指针,一个指向栈顶,一个指向栈顶的下一个元素
PNODE q=NULL;
while(p!=ps->pBottom){
q=p->pNext; //q指向p的下一个节点
free(p); //清空p
p=q; //将q赋予p
}
ps->pTop=ps->pBottom;
}
}
线性结构的两种常见应用之一 队列
定义:
一种可以实现“先进先出”的存储结构
分类
链式队列 – 》 用链表实现
静态队列 --》 用数组实现
静态队列一般都必须是循环队列
循环队列
需要两个参数来确定
front
rear
这两个参数在不同的场合有不同的含义
(1),队列初始化
front和rear的值都是零
(2),队列非空
front代表的是队列的第一个元素
rear代表的是队列的最后一个有效元素的下一个元素
(3),队列空
front和rear的值相等,但不一定是零
循环队列入队伪算法
将值存入r所代表的位置
rear=(rear+1)%数组的长度
判断循环队列是否为空
front = rear 的值相等该队列为空
判断循环队列是否已满
两种方式
1,多增加一个表标识参数
2,少用一个元素
如果r和f的值相邻,则队列已满
if((r+1)%数组的长度==f){
已满
}else
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
typedef struct Queue{
int *pBase;
int front;
int rear;
}QUEUE;
void init(QUEUE*);
bool en_queue(QUEUE*,int);//在哪里放,要放的值
void traverse_queue(QUEUE*);
bool full_queue(QUEUE *);
bool out_queue(QUEUE*,int*);
bool empty_queue(QUEUE*);
int main(void){
int val;
QUEUE Q;
init(&Q);
en_queue(&Q,1);
en_queue(&Q,2);
en_queue(&Q,3);
en_queue(&Q,4);
en_queue(&Q,5);
en_queue(&Q,6);
traverse_queue(&Q);
if(out_queue(&Q,&val)){
printf("出队成功,队列出队的元素:%d\n",val);
}else{
printf("出队失败\n");
}
traverse_queue(&Q);
en_queue(&Q,6);
traverse_queue(&Q);
return 0;
}
void init(QUEUE* pQ){
//为数组分配空间 长度为6
pQ->pBase=(int*)malloc(sizeof(int)*6);
pQ->front=0;
pQ->rear=0;
}
bool en_queue(QUEUE* pQ,int val){
//判断是否已满
if(full_queue(pQ)){
return false;
}else{
pQ->pBase[pQ->rear]=val;
pQ->rear=(pQ->rear+1)%6;
return true;
}
}
bool full_queue(QUEUE * pQ){
if((pQ->rear+1)%6==pQ->front)
return true;
else
return false;
}
void traverse_queue(QUEUE* pQ){
int i=pQ->front;
while(i!=pQ->rear){
printf("%d ",pQ->pBase[i]);
i=(i+1)%6;//后移动一位
}
printf("\n");
return;
}
bool empty_queue(QUEUE* pQ){
if(pQ->front==pQ->rear)
return true;
else
return false;
}
bool out_queue(QUEUE* pQ,int* pVal){
if(empty_queue(pQ)){
return false;
}else{
*pVal=pQ->pBase[pQ->front];
pQ->front=(pQ->front+1)%6;
return true;
}
}
递归
#include <stdio.h>
void fun(){
int val;
int i,mult=1;
scanf("%d",&val);
for(i=1;i<=val;i++)
mult*=i;
printf("%d\n",mult);
}
long f(long i){
if(i==1){
return 1;
}else{
return i*f(i-1);
}
}
long sum(long i){
if(i==1)
return 1;
else
return i+sum(i-1);
}
int main(void){
//fun();
long i=f(5);
printf("%ld\n",i);
long s=sum(100);
printf("%ld\n",s);
return 0;
}
快速排序
#include <stdio.h>
void QuickSort(int *a,int low,int high);
int FindPos(int *a,int low,int high);
int main(void){
int a[6]={2,1,0,5,4,3};
QuickSort(a,0,5);
for(int i=0;i<6;i++)
printf("%d",a[i]);
return 0;
}
void QuickSort(int *a,int low,int high){
int pos;//下标
if(low<high){
pos=FindPos(a,low,high);
QuickSort(a,low,pos-1);
QuickSort(a,pos+1,high);
}
}
int FindPos(int *a,int low,int high){
int val=a[low];
while(low<high){
while(low<high && a[high]>=val){
--high;
}
a[low]=a[high];
while(low<high && a[low]<=val){
++low;
}
a[high]=a[low];
}//while循环之后low和high的值一定是相等的
a[low]=val;
return low;//程序执行完成后low==high==第一个元素的真实位置
}