【算法】大话数据结构学习笔记

前言

数据结构是学习编程的必要基础之一,不把这个搞定将来真的很难有大的发展。。很多招聘企业在应聘要求中都写有熟悉数据结构与算法,去研究一种更好的新算法不是我能做到的,把前人研究过并且已经得到验证和实际使用的算法学会就好,如果能运用到自己的工作学习中就更好了。
这本书的特点是比较俏皮,像是聊天讲故事,但是刚开始的基本属于实在是绕人。数据、数据元素、数据项、数据对象、数据结构这些看着就头疼。http://www.cnblogs.com/zhanggaofeng/p/5672610.html 这篇博客讲的很清楚。
在这里插入图片描述
关于逻辑结构和物理结构的区别,下图取自 https://blog.youkuaiyun.com/qq_32623363/article/details/79198037 我觉得可能书里将的会比较合理一些,逻辑结构涉及到的是数据的内容,逻辑结构是由数据的内容决定的;而物理结构是单纯的数据在内存中存储的结构,和数据本身的内容关系不大。
在这里插入图片描述

算法是解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作
算法具有五个基本特性: 输入、输出、有穷性、确定性和可行性。
算法设计的要求:正确性、可读性、健壮性、时间效率高和存储量低

计算算法的时间复杂度其实是一个很基础的东西,目前看主要就是先看执行次数和问题规模的函数f(n),然后推导大O阶的方法计算出时间复杂度,其实只需要关注f(n)中的最高阶的阶数,因为在问题规模趋向于无限大的时候,起决定性作用的就是这个最高阶。计算方法如下:
在这里插入图片描述

三、线性表

定义:0个或多个数据元素的有限序列
线性表的顺序存储结构最典型的就是数组,可想而知,在知道元素的位置时,读写元素是非常快的,但是对于插入和删除就会很麻烦,所以它比骄适合数据数目变化不大但是要经常读取的情况;
链式存储结构是采用的p->data p->next这样的结构进行数据的存储的,容易忽略的一点是,对于在链表中插入和删除数据本身操作随便比较简单但是找到这个元素还是要从头开始查找;
静态链表是一个比较新颖的方式,是在数组中实现类似于链表的机制,要点就是头元素和尾元素留出来保存链表的头元素以及空链表的头元素;
双向链表其实就是相比单向链表多了一个前驱指针;这里面比较重要的一点是**”循环链表“**也就是将链表尾部的元素的next指针改为头元素实现一个循环链表;

四、栈和队列

定义:限制了只能在表头和表尾进行操作的线性表

  1. 顺序存储的栈
    本质上就是一个数组
    例子:对于栈空间为5的栈,空栈和满栈的情况如下:里面比较重要的和汇编里讲的一样,主要是看栈顶指针,时刻指向栈顶元素,所以当空栈时,栈顶指针指向的是-1;
    在这里插入图片描述
    两栈共享空间适用于此消彼长的情况,一个大的数组头和尾分别作为两个栈的栈底;

  2. 链式存储的栈——链栈
    比较关键的点在于,链栈的栈顶就是单链表的表头。一般单链表会有一个头结点,它保存着链表的一些基本参数,而它的next指针指向链表的第一个节点,如果链表为空则头结点的next指针指向null;而链栈中,头结点其实就是我的栈,如下图:可以看到,LinkStack本质上就是头结点,它保存着栈中元素数目,而top指针指向链表的第一个元素也就是栈顶元素。因此栈为空也就意味着top=null;
    在这里插入图片描述
    下面是链栈的出栈操作,其中值得注意的是对栈空的检验,二是释放栈顶元素的时候,申明一个新的指针指向栈顶元素然后释放该指针就可以释放对应的空间;
    在这里插入图片描述

  3. 顺序队列
    有点类似于接收帧数据解包的环形缓冲池,它的原理就是循环顺序队列;

  4. 链队列
    本质上就是一个只能在头部删除节点和尾部插入节点的单链表。保留链表的头结点,里面可以保存队列数目,next指针指向队列的第一个元素,实际上链队列的结构体只包含两个结点指针,front指向头结点,它不是第一个元素!rear指向最后一个指针,需要注意的地方就是,当队列中只剩下一个元素而且要将其出队列时,需要将rear指向修改为和front相同,不然的话rear的指向就是错误的了。可以在每次出队的时候加一个判断,看这个要出对的节点是不是rear指向的节点,是的话就要修改rear和front相同。
    在这里插入图片描述

五、串

比较有趣的一个记忆字符串大小的方法,就是假想成一本字典,越往后越大。字符串的处理不同的语言基本上是相似的,查找、合并、子串等等。

KMP模式匹配算法

对于朴素的模式匹配算法,将子串的首字符与大串的字符进行逐个比较,整个比较过程文字描述比较困难,还好比较容易想到它的实现过程。KMP的思想就是先去考察子串的结构,先分析出子串中各个位置字符的相似程度再根据此进行相应的处理。算法就是针对一个字符串生成一个next数组:
在这里插入图片描述
该书中提供的KMP计算next数组的函数中,数组和字符串的索引竟然是从1开始的。对代码进行了测试发现的确是不行。下图中采用的例子是书上提供的。正确的next结果应当是012345678.很明显就错在索引上,因此需要对这个代码进行修改。将sublime配置成C++编译器很简单,之前的博客中也有写:【工具】Subline配置为C++编译器
在这里插入图片描述

六、树

只有一个根节点,它的所有子树互不相交。树有很多属性和性质,比如深度、度、双亲、孩子之类的,一般看一遍就能理解。关于二叉树以及完全二叉树,它们有更多的特殊属性,我觉得知道就可以,有些可能会在实际应用中用到。接下来是关于树的存储结构:
首先,完全二叉树适合顺序存储,这个是比较好理解的。而对于一般的二叉树,显然使用链式结构更合适。
在这里插入图片描述

二叉树的遍历

方法比较绕人,我总结的规律是:

  1. 前序遍历:任意一个结点,先遍历自己,再遍历左子树,最后遍历右子树。这里比较重要的是,节点的任意性,也就是说,当从根节点出发的时候,先取根节点、再取根节点的左子树,到了左子树,下一步应当是左子树的左子树而不是根节点的右子树;
  2. 中序遍历:任意一个结点,先遍历左子树,再遍历自己,最后遍历右子树。这里同样和上面一样,重要的地方在于对于左子树,它也可能会有左子树。这里会有一个嵌套的过程;
  3. 后序遍历:任意一个结点,先遍历左子树,再遍历右子树,最后遍历自己。和上面的都一样,嵌套决定了根节点是最后一个被遍历的;

理解了上面的内容后,其实很容易想到遍历二叉树的算法应当怎么写。首先,从上面的分析可以看到,其实根节点的左子节点其实是左子树的根节点,这就意味着需要再对这个节点进行一次左子节点的遍历。因此会用到递归调用:
二叉树的数据结构如下:

typedef struct BitNode		//这是一个二叉树中的节点
{
   
   
	datatype data;		//存放节点中实际的数据						
	struct  BitNode *lchild,*rchild;	//左右孩子节点
}BitNode,*BitTree;		//创建一个结构体指针只需要使用BitTree即可
//前序遍历:可以看出,是先对某个节点遍历,然后遍历它的左子节点,而后是右子节点
void PreOrderTraversing(BitTree T)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值