概述
现在是大数据时代,如何高效地处理大量的数据呢?
数据结构的作用:研究数据如何在计算机中进行组织和存储的,使得我们可以高效的获取数据或者修改数据
特点:数学和计算机的结合,代码量大,可以极大地锻炼逻辑思考能力
起源:
早期计算器: 数学---纯数字之间的关系 +-*/ tan log e, 而今天的数据早已经不再是纯数字的关系
数据结构:研究数据与数据之间关系的一门学科 数据=数字 + 非数字,操作:增删改查
数据和数据之间的关系: 从两个角度研究数据和数据之间的关系
1.人的角度--理论, 逻辑关系--不依赖于计算机
线性关系:一个前驱,一个后继 一对一关系 (一个队列或队伍)
树关系: 一个前驱,多个后记 一对多关系
网络关系: N个前驱,N个后继 多对多关系
2.计算机的角度
存储关系 : 研究逻辑关系如何存储的
数据与数据之间的关系是静态的。 算法:处理数据和数据关系(的程序)是动态的
//实现任务:1+2+3+....100
//方法一:
for(int i=1;i<=100;i++) sum += i;
//方法二:
(1+最后一个数)*个数/2
算法不是唯一的:解决一个问题有n种算法
如何衡量一个算法执行优劣呢?
从两个方面
1.时间 ---时间复杂度来衡量
指令的频度:一个算法执行的总次数(输入量的公式)
时间复杂度: 当数据趋于无穷的时候,频度就是时间复杂度,
PD = n^3+ n^2 +n^2 +n + n+1 ,当n->无穷, 时间复杂度 = n^3
它是衡量当数据量为无穷的时候,消耗时间的多少
设计算法考虑:数据的规模, 时间复杂度
有哪些时间复杂度?
常量复杂度 o(1) :推荐,输入量增加,算法时间恒定
线性复杂度o(n) :数量量增加,运算时间线性增加
n^2复杂度o(n^2) :比如两个for
log复杂度o(log) :树的操作
2.空间---空间复杂度
一般我们很少在意空间问题,一般只从时间复杂度考虑问题
当输入量/数据量趋于无穷的时候,程序所占用的空间的大小
数据与数据之间的关系
一、线性关系
特点:一个节点,最多一个前驱,一个后继。头部,无前驱,尾部无后继
线性关系的存储:
1.顺序存储:逻辑上相邻,存储上也相邻。(数组)
优点:可以随机访问
缺点:删除和插入,需要搬运大量数据,很耗时,不划算。
数组是一次性分配的,可能浪费不够用
2.链式存储:逻辑上相邻,存储上不相邻 (链表)
不能随机访问,只能通过前一个结点,找到下一个节点,
每个节点,除了存放自己的数据之外,还要存放下一个结点的地址
优点:按需分配空间,插入删除不需要大量移动
栈:stack
栈是一种 先进后出 的线性结构,比如电梯,球桶,死胡同
只在一端进行插入和删除的线性表,称为栈
作用:对数组进行逆序
1.顺序栈--数组 2.链式栈--链表
队列:queue 又称FIFO,first input first ouput
只允许在两端 进行操作,尾部插入,头部删除的一种线性表
作用: 缓存匹配输入和输出两种不同的速度
1.顺序存储---使用普通数组实现,发现删除数据的时候,大量的搬移数据
变通的方法:环形队列
2.链式存储---就是使用链表完成FIFO
二、树关系
树
定义:有且只有一个根,其他分支互不相交。
每个节点最多只有一个前驱,可以有多个后继
根节点:跟无前驱,叶子节点,无后继
节点的度数:节点的直接子树个数。父节点,子节点: 是相对的
兄弟节点:拥有相同父亲的节点,互为兄弟节点
层次: 根节点层次是1,每经过一个子节点+1. 深度:最大的层次数
用途: 计算机中的目录
二叉树
每个节点最多有两个两个子节点,称为左和右
二叉树具有树的一切特性,所以它可以实现树的任何操作
我们一般使用二叉树代替树结构
满二叉树: 每个节点都有两个子节点,叶子节点除外
完全二叉树: 满二叉树,只有最后一层(叶子节点),不完整
二叉树的遍历:
遍历:访问只访问一次
三种遍历方法:
先序遍历: 跟左右 ,先访问跟,然后左和右 ,跟始终在前面
中序遍历:左根右 ,先访问左,然后是跟和右, 跟始终在中间
后序遍历:左右跟 ,跟始终在后面
先序和后序可以知跟,中序分左右
二叉树的存储:
线性存储,将二叉树布满为满二叉树,然后编号,
根据编号依次放入一个伟大的数组,如果是一个空节点,存放-1
特点:浪费太大---没有实用价值
链式存储:画图分析
#include <stdio.h>
struct bnode{
char ch; //存放数据
struct bnode *left,*right; //存放左右子节点的地址
};
struct bnode a,b,c,d,e;
struct btree_front_traval(struct bnode *root)
{
if(root ==NULL){
return ;
}
printf("%c ",root->ch);
btree_front_traval(root->left);
btree_front_traval(root->right); //递归
}
int main()
{
a.ch='A'; b.ch='B'; c.ch='C'; e.ch='E';
a.left=&b; a,right=NULL:
b.left=&c; b.right=&d;
c.left=NULL; c.right=&NULL;
d.left=&e; d.right=NULL;
e.left=NULL; e.right=NULL;
struct bnode *root = &a;
printf("front traval:");
btree_front_traval(root);
printf("\n");
return o;
}
二叉树的应用:
哈夫曼树,最优二叉树
用途:文件的压缩
方法: 定长编码 :每个字符占据的bit数,是固定的
不定长编码:
原理:一篇文章,各个字符出现的概率是不一样,如果给概率高的字符一个很短的编码
该概率低的字符,一个很长的编码,整片文件所占的空间将被大大压缩。
如何实现对文章的最优编码:
1.扫描整篇,找出文章中所用到的字符
计算各个字符出现的 概率/频次
2.以这些字符为节点,构建哈夫曼树
3.所有左侧分支 安排0,右侧分支 安排1,则得到编码
图
特点:节点N个前驱,N个后继
用途:计算机网络 (人际关系网,导航系统.....)
分类:
有向图: 节点间单向行走 无向图:双向行走
权:权值--两个节点间的距离/边的长度
度:和某个节点直接相连的点,成为该结点的度数 在有向图里,又分为入度和出度
路径:从一个节点到另一个节点,所经过的所有节点集合成为路径
查找和排序
1.遍历方法
领域: 数据量比较小,数据无规律
2.二分查找
领域:适合有规律的数据,排序好的数据
#include <stdio.h>
int arr[10] = {2,4,5,7,8,9,11,12,18,22};
int bin_find(int *arr, int cnt, int findval)
{
int low=0;
int high =cnt-1;
int mid;
while(1){
mid = (low+high)/2;
if( arr[mid] == findval ){
return mid;
}
if( arr[mid] > findval ){
high=mid-1;
}
if( arr[mid] < findval ){
low=mid+1;
}
}
//如果程序到这里 这个要找的数就不存在 ,low > high
retuen -1;
}
排序:
选择排序
for(k=0;k<N-1;k++){
minpos=k; //遍历之前假设第一个就是最小值
for(i=k;i<=N-1;i++){
if( arr[i] < arr[minpos] ){
minpos=i;
}
}
//congratulation,minpos就是最小值,和k位置交换
tmp=arr[k];arr[k]=arr[minpos];arr[minpos]=tmp;
}
冒泡排序
for(int k=0;k<=N-2;k++){
for(int i=0;i<=N-k-2;i++){
if(arr[i] > arr[i+1]){
int tmp=arr[i];arr[i]=arr[i+1];arr[i+1]=tmp;
}
}
}
快速排序
插入排序----链表
总结
数据结构的学习过程中,画图来帮助思考是很有必要的
我这里只是一个大的框架,很多地方还需要去扩展和延申,可能会出现很多不同的情况