跟着郝斌学数据结构(01)——预备

来源:互联网 发布:跨三层取mac 编辑:程序博客网 时间:2024/06/06 12:49
数据结构概述
定义

我们如何把现实中大量而复杂的问题以特定的数据类型和特定的存储结构保存到主存储器(内存)中,以及在此基础上为实现某个功能(比如查找某个元素,删除某个元素,对所有元素进行排序)而执行的相应操作, 这个相应的操作也叫算法。

特定的数据类型和结构是指:如果我们保存少量的数据,我们可以使用数组(连续);如果保存大量的数据,我们就必须使用链表(不连续,通过指针相连);如果我们还要保存数据item之间的关系,如一个部门的上下级关系,我们就必须使用树来保存;如果我们要保存一个城市的地图(任何节点之间都可能产生关系),那我们必须使用图来保存。

书:严蔚敏、吴伟民(伪算法)   高一凡(算法的实现)
数据结构 = 个体的存储 + 个体关系的存储
算法 = 对存储数据的操作

算法
解题的方法和步骤

衡量算法的标准
1. 时间复杂度
大概程序要执行的次数,而非执行的时间,因为机器执行的速度不一样
2. 空间复杂度
算法执行过程中大概所占用的最大内存
3. 难易程度
4. 健壮性

数据结构的地位
数据结构是软件中最核心的课程
程序 = 数据的存储 + 数据的操作 + 可以被计算机执行的语言


预备知识
指针

指针的重要性:

表示一些复杂的数据结构  
快速的传送数据
使函数返回一个以上的值
能否直接访问硬件    
能够方便的使用数组和字符串
是理解面向对象语言中引用的基础

指针是C语言的灵魂
定义
地址
地址就是内存单元的编号
从0开始的非负整数
范围: 0 -- FFFFFFFF【0-4G-1】

指针:
指针就是地址 地址就是指针
指针变量是存放内存单元地址的变量

指针的本质是一个操作受限的非负整数

int * p; //p是个变量名字, int * 表示该p变量只能存储int类型变量的地址int i = 10;int j;p = &i;*p = i; // 等价于 i = i;j = *p; // 等价于 j = i;如果上一行注释掉,即没有给p赋值,那么我们就不能获取*p,因为你不知道p指向哪儿了。printf("i = %d, j = %d, *p = %d\n", i, j, *p); //p = 10;  //error



分类:
1. 基本类型的指针
int i=10;
int *p = &i; //等价于 int *p;p = &i;
详解这两部操作:
1)、p存放了i的地址,所以我们说p指向了i
2)、p和i是完全不同的两个变量,修改其中的任意一个变量的值,不会影响另一变量的值
3)、p指向i,*p就是i变量本身。更形象的说所有出现*p的地方都可以换成i,所有出现i
的地方都可以换成*p

总结: 1、如何一个指针变量(假定为p)存放了某个普通变量(假定为i)的地址,
那我们就可以说:“p指向了i”, 但p与i是两个不同的变量,
修改p的值不影响i的值,修改i的值不影响p的值.
2、 *p等价于i  或者说*p可以与i在任何地方互换
3、 如果一个指针变量指向了某个普通变量,则
*指针变量  就完全等价于  该普通变量
注意:
指针变量也是变量,只不过它存放的不能是内存单元的内容,只能存放内存单元的地址
普通变量前不能加*
常量和表达式前不能加&

如何通过被调函数修改主调函数中普通变量的值
实参为相关变量的地址
形参为以该变量的类型为类型的指针变量

在被调函数中通过  *形参变量名 的方式就可以修改主函数相关变量的值

void f(int * p) //不是定义了一个名字叫做*p的形参, 而是定义了一个形参,该形参名字叫做p,它的类型是int *{*p = 100; //}int main(void){int i = 9;f(&i);printf("i = %d\n", i);return 0;}


2  指针和数组的关系
指针 和 一维数组
数组名

一维数组名是个指针常量,它存放的是一维数组第一个元素的地址, 它的值不能被改变一维数组名指向的是数组的第一个元素

下标和指针的关系
a[i] <<==>> *(a+i)

假设指针变量的名字为p
则p+i的值是p+i*(p所指向的变量所占的字节数)
指针变量的运算
指针变量不能相加,不能相乘,不能相除
如果两指针变量属于同一数组,则可以相减
指针变量可以加减一整数,前提是最终结果不能超过指针允许指向的范围
p+i的值是p+i*(p所指向的变量所占的字节数)
p-i的值是p-i*(p所指向的变量所占的字节数)
p++  <==> p+1

p--<==>   p-1

int a[5] = {1,2,3,4,5};//a[3] == *(3+a);printf("%p\n", a+1);printf("%p\n", a+2);//以上两个输出地址,是连续的,因为整形占据四个字节,所以他们之间相差4.printf("%d\n", *a+3); //*a+3等价于 a[0]+3

举例

如何通过被调函数修改主调函数中一维数组的内容【如何界定一维数组】
两个参数
存放数组首元素的指针变量

存放数组元素长度的整型变量

void Show_Array(int * p, int len){int i = 0;for (i=0; i<len; ++i)printf("%d\n", p[i]);//p[2] = -1;  //p[0] == *p   p[2] == *(p+2) == *(a+2) == a[2]//p[i]就是主函数的a[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]);return 0;}



double * p;double x = 66.6;p = &x;  //x占8个字节  1个字节是8位, 1个字节一个地址double arr[3] = {1.1, 2.2, 3.3};double * q;q = &arr[0];printf("%p\n", q);  //%p实际就是以十六进制输出q = &arr[1];printf("%p\n", q); //他们之间相差8


通过函数修改实参的值只能通过传输地址的方式,即使是int *类型。
void f(int ** q);int main(void){int i = 9;int * p = &i;// int  *p;  p = &i;printf("%p\n", p);f(&p);//修改p的值也只能通过传输其地址的方式,因为指针变量也是一个变量,存放一个地址,占用四个字节。printf("%p\n", p);return 0;}void f(int ** q){*q = (int *)0xFFFFFFFF;}


结构体

为什么出现结构体:为了表示一些复杂的数据,而普通的基本类型无法满足要求。

什么是结构体呢:用户自定义的复合数据类型

如何使用结构体:一种是“.”的方式,一种是指针的方式。

struct Student{int sid;char name[200];int age;}; //分号不能省int main(void){struct Student st = {1000, "zhangsan", 20};printf("%d  %s  %d\n", st.sid, st.name, st.age);st.sid = 99;//st.name = "lisi";  //errorstrcpy(st.name, "lisi");st.age = 22;printf("%d  %s  %d\n", st.sid, st.name, st.age);//printf("%d %s %d\n", st);  //errorreturn 0;}


struct Student{int sid;char name[200];int age;}; //分号不能省int main(void){struct Student st = {1000, "zhangsan", 20};//st.sid = 99;  //第一种方式struct Student * pst;pst = &st;pst->sid = 99;  //第二种方式  pst->sid 等价于 (*pst).sid  而(*pst).sid等价于 st.sid,  所以pst->sid 等价于 st.sidreturn 0;}
注意事项:结构体不能加减乘除,但是可以相互赋值。

传输的时候最好传输指针:地址,而不要传输变量,因为变量可能需要传输很多字节,而指针始终传输的是四个字节。

# include <stdio.h># include <string.h>struct Student{int sid;char name[200];int age;}; //分号不能省void f(struct Student * pst);void g(struct Student st);void g2(struct Student *pst);int main(void){struct Student st;  //已经为st分配好了内存f(&st);g2(&st);//printf("%d %s %d\n", st.sid, st.name, st.age);return 0;}//这种方式耗内存 耗时间 不推荐void g(struct Student st){printf("%d %s %d\n", st.sid, st.name, st.age);}void g2(struct Student *pst){printf("%d %s %d\n", pst->sid, pst->name, pst->age);}void f(struct Student * pst){(*pst).sid = 99;strcpy(pst->name, "zhangsan");pst->age = 22;}


动态内存的分配和释放

动态构造一维数组
假设动态构造一个int型数组
int *p = (int *)malloc(int len);
1、 malloc只有一个int型的形参,表示要求系统分配的字节数
2、 malloc函数的功能是请求系统len个字节的内存空间,如果请求分配成功,
则返回第一个字节的地址,如果分配不成功,则返回NULL
3、 malloc函数能且只能返回第一个字节的地址,所以我们需要把这个无任何实
际意义的第一个字节的地址(俗称干地址)转化为一个有实际意义的地址,因此
malloc前面必须加(数据类型 *),表示把这个无实际意义的第一个字节的地址
转化为相应类型的地址。如:
int *p = (int *)malloc(50); 
     表示将系统分配好的50个字节的第一个字节的地址转化为int *型的
     地址,更准确的说是把第一个字节的地址转化为四个字节的地址,这
     样p就指向了第一个的四个字节,p+1就指向了第2个的四个字节,
     p+i就指向了第i+1个的4个字节。p[0]就是第一个元素, p[i]就是第
     i+1个元素
double *p = (double *)malloc(80); 
     表示将系统分配好的80个字节的第一个字节的地址转化为double *型的
     地址,更准确的说是把第一个字节的地址转化为8个字节的地址,这
     样p就指向了第一个的8个字节,p+1就指向了第2个的8个字节,
     p+i就指向了第i+1个的8个字节。p[0]就是第一个元素, p[i]就是第
     i+1个元素
free(p)
释放p所指向的内存,而不是释放p本身所占用的内存


模块一: 线性结构

连续存储[数组]

离散存储[链表]

线性结构的两种常见应用之一 栈

线性结构的两种常见应用之二 队列

专题: 递归

1. 1+2+3+4+...100的和
2. 求介乘
3. 汉诺塔
4. 走迷宫

模块二: 非线性结构




模块三: 查找和排序
折半查找

排序:
冒泡
插入
选择
快速排序

归并排序

Java中容器和数据结构相关知识
Iterator接口
Map
哈希表

0 0
原创粉丝点击