算法与数据结构

递归和折半查找

递归

递归(recursion)是一种常见的解决问题的方法,即把问题逐渐简单化
递归的基本思想就是“自己调用自己”,一个使用递归技术的方法将会直接或者间接的调用自己
利用递归可以用简单的程序来解决一些复杂的问题。比如:斐波那契数列的计算、汉诺塔、快速排序等问题

在这里插入图片描述

递归问题的特点

  • 一个问题可被分解为若干层简单的子问题
  • 子问题和其上层问题的解决方案一致
  • 外层问题的解决依赖于子问题的解决

递归结构包括两个部分

  • 递归结束条件:什么时候不调用自身方法。如果没有条件,将陷入死循环。
  • 递归体。解答:什么时候需要调用自身方法。

递归的优点

  • 自然的思路,简单的程序

递归的缺点

  • 但是递归调用会占用大量的系统堆栈,内存耗用多,
  • 在递归调用层次多时速度要比循环慢的多

注意事项

  • 任何能用递归解决的问题也能使用迭代解决。当递归方法可以更加自然地反映问题,并且易于理解和调试,并且不强调效率问题时,可以采用递归。
  • 在要求高性能的情况下尽量避免使用递归,递归调用既花时间又耗内存。

折半查找

折半查找又称为二分查找,这种查找方法需要待查的查找表满足两个条件:
首先,查找表必须使用顺序存储结构;
其次,查找表必须按关键字大小有序排列。

在这里插入图片描述

public class TwoTest {
    public static void main(String[] args) {
        //折半查找前提为数组必须是有顺序的
        int[] random = {3, 8, 23, 47, 59, 63, 82, 94, 100};
        int key = 1;
        System.out.println("查询下标为:" + getIndex(random, key));
    }
    
    public static int getIndex(int[] random, int key) {
        //判断数组是否为空
        if (random == null || random.length == 0) {
            return -1;
        }
        //开始下标
        int start = 0;
        //结束下标
        int end = random.length - 1;
        //当结束下标小于开始下标时结束
        while (start <= end) {
            //中间下标
            int mid = (start + end) >>> 1;
            //判断中间是否为查询值
            if (key == random[mid]) {
                return mid;
                //查询值大于中间值,换开始下标
            } else if (key > random[mid]) {
                start = mid + 1;
            } else {
                //查询值小于中间值,换结束下标
                end = mid - 1;
            }
        }
        return -1;
    }
}

数据结构基础

什么是数据结构

数据结构 (不是建筑结构、人体结构)

数据结构(data structure)是指相互间存在一种或多种特定关系的数据元素的集合
是组织并存储数据以便能够有效使用的一种专门格式,它用来反映一个数据的内部构成,即一个数据由那些成分数据构成,以什么方式构成,呈什么结构

  • 由于信息可以存在于逻辑思维领域,也可以存在于计算机世界,因此作为信息载体的数据同样存在于两个世界中。
  • 表示一组数据元素及其相互关系的数据结构同样也有两种不同的表现形式,
  • 一种是数据结构的逻辑层面,即数据的逻辑结构;
  • 一种是存在于计算机世界的物理层面,即数据的存储结构。

数据结构=逻辑结构+存储结构+(在存储结构上的)运算/操作

在这里插入图片描述

数据的逻辑结构

数据的逻辑结构指数据元素之间的逻辑关系(和实现无关)。
线性结构:有且只有一个开始结点和一个终端结点,并且所有结点都最多只有一个直接前驱和一个直接后继。

线性表就是一个典型的线性结构,它有四个基本特征:

  1. 集合中必存在唯一的一个"第一个元素";
  2. 集合中必存在唯一的一个"最后的元素";
  3. 除最后元素之外,其它数据元素均有唯一的"直接后继";
  4. 除第一元素之外,其它数据元素均有唯一的"直接前驱"。

线性结构:数据元素之间存在着"一对一"的线性关系的数据结构。
在这里插入图片描述

树状结构:除了一个数据元素(元素 01)以外每个数据元素有且仅有一个直接前驱元素,但是可以有多个直接后续元素。
特点是数据元素之间是 1 对 多的联系
在这里插入图片描述

网络结构:每个数据元素可以有多个直接前驱元素,也可以有多个直接后续元素。特点是数据元素之间是多对 多 的联系
在这里插入图片描述

数据的存储结构

数据的存储结构主要包括数据元素本身的存储以及数据元素之间关系表示,是数据的逻辑结构在计算机中的表示
包括 顺序存储、链式存储、索引存储,以及散列存储四种

顺序存储结构:把逻辑上相邻的节点存储在物理位置上相邻的存储单元中,结点之间的逻辑关系由存储单元的邻接关系来体现。
由此得到的存储结构为顺序存储结构,通常顺序存储结构是借助于计算机程序设计语言(例如C/C++)的数组来描述的。
(数据元素的存储对应于一块连续的存储空间,数据元素之间的前驱和后续关系通过数据元素,在存储器中的相对位置来反映)
在这里插入图片描述

链式存储结构:数据元素的存储对应的是不连续的存储空间,每个存储节点对应一个需要存储的数据元素。每个结点是由数据域和指针域组成。元素之间的逻辑关系通过存储节点之间的链接关系反映。逻辑上相邻节点物理上不必相邻。
在这里插入图片描述

索引存储结构:除建立存储结点信息外,还建立附加的索引表来标识结点的地址。
比如图书、字典的目录
在这里插入图片描述

散列存储结构:根据结点的关键字直接计算出该结点的存储地址,比如Java中的HashSet、HashMap底层就是散列存储结构。这是一种神奇的结构,添加、查询速度快。
在这里插入图片描述

注意

  1. 同一逻辑结构可以对应多种存储结构。
  2. 同样的运算,在不同的存储结构中,其实现过程是不同的

线性表

线性表是n个类型相同数据元素的有限序列,通常记作(a0 , a 1 , …a i-1 , a i , a i+1 …,a n-1 )

相同数据类型

从a 0到a n-1 的n个数据元素是具有相同属性的元素。
比如说可都是数字,例如(23, 14, 66, 5, 99);也可以是字符,例如(A, B, C, … Z);
当然也可以是具有更复杂结构的数据元素,例如学生、商品、装备。

序列(顺序性)

相同数据类型意味着在内存中存储时,每个元素会占用相同的内存空间,便于后续的查询定位。
在线性表的相邻数据元素之间存在着序偶关系,
即a i-1 是a i 的直接前驱,则a i 是a i-1 的直接后续,同时a i 又是a i+1 的直接前驱,a i+1 是a i 的直接后续。
唯一没有直接前驱的元素a 0称为表头,唯一没有后续的元素a n-1称为表尾。
除了表头和表尾元素外,任何一个元素都有且仅有一个直接前驱和直接后继。

有限

线性表中数据元素的个数n定义为线性表的长度,n是一个有限值。
当n=0 时线性表为空表。
在非空的线性表中每个数据元素在线性表中都有唯一确定的 序号,例如a0 的序号是0,ai 的序号是i。
在一个具有n > 0 个数据元素的线性表中,数据元素序号的范围是[0, n-1]。

  • 线性表逻辑结构对应的顺序存储结构为顺序表,对应的链式存储结构为链表。

顺序表----顺序存储结构

特点:在内存中分配连续的空间,只存储数据,不需要存储地址信息。位置就隐含着地址。

优点

  1. 节省存储空间,因为分配给数据的存储单元全用存放结点的数据(不考虑c/c++语言中数组需指定大小的情况),结点之间的逻辑关系没有占用额外的存储空间。
  2. 索引查找效率高,即每一个结点对应一个序号,由该序号可以直接计算出来结点的存储地址。
      假设线性表的每个数据元素需占用K个存储单元,并以元素所占的第一个存储单元的地址作为数据元素的存储地址。则线性表中序号为i的数据元素的存储地址LOC(ai)与序号为i+1 的数据元素的存储地址LOC(a i+1)之间的关系为
    LOC(a i+1 ) = LOC(ai ) + K
      通常来说,线性表的i号元素a i 的存储地址为
    LOC(a i ) = LOC(a 0 ) + i×K
    其中LOC(a 0 )为 0 号元素a 0 的存储地址,通常称为线性表的起始地址。

缺点

  1. 插入和删除操作需要移动元素,效率较低。
  2. 必须提前分配固定数量的空间,如果存储元素少,可能导致空闲浪费。
  3. 按照内容查询效率低,因为需要逐个比较判断

链表----链式存储结构

特点:数据元素的存储对应的是不连续的存储空间,每个存储结点对应一个需要存储的数据元素。每个结点是由数据域和指针域组成。 元素之间的逻辑关系通过存储节点之间的链接关系反映出来。逻辑上相邻的节点物理上不必相邻。

缺点

  1. 比顺序存储结构的存储密度小 (每个节点都由数据域和指针域组成,所以相同空间内假设全存满的话顺序比链式存储更多)。
  2. 查找结点时链式存储要比顺序存储慢(每个节点地址不连续、无规律,导致按照索引查询效率低下)。

优点

  1. 插入、删除灵活 (不必移动节点,只要改变节点中的指针,但是需要先定位到元素上)。
  2. 有元素才会分配结点空间,不会有闲置的结点。
  • 在使用单链表实现线性表的时候,为了使程序更加简洁,我们通常在单链表的最前面添加一个哑元结点,也称为头结点。
  • 在头结点中不存储任何实质的数据对象,其 next 域指向线性表中 0 号元素所在的结点,
  • 可以对空表、非空表的情况以及对首元结点进行统一处理,编程更方便,常用头结点。

其他链表

双向链表

单链表一个优点是结构简单,但是它也有一个缺点,即在单链表中只能通过一个结点的引用访问其后续结点,而无法直接访问其前驱结点
要在单链表中找到某个结点的前驱结点,必须从链表的首结点出发依次向后寻找,但是需要Ο(n)时间
为此我们可以扩展单链表的结点结构,使得通过一个结点的引用,不但能够访问其后续结点,也可以方便的访问其前驱结点
扩展单链表结点结构的方法是,在单链表结点结构中新增加一个域,该域用于指向结点的直接前驱结点。扩展后的结点结构是构成双向链表的结点结构
在双向链表中同样需要完成数据元素的查找、插入、删除等操作
在双向链表中进行查找与在单链表中类似,只不过在双向链表中查找操作可以从链表的首结点开始,也可以从尾结点开始,但是需要的时间和在单链表中一样
Java中的LinkedList底层使用的就是双向链表

循环链表

在一个循环链表中, 首节点和末节点被连接在一起。这种方式在单向和双向链表中皆可实现。要遍历一个循环链表,你开始于任意一个节点然后沿着列表的任一方向直到返回开始的节点。循环链表可以被视为"无头无尾"
循环链表中第一个节点之前就是最后一个节点,反之亦然
循环链表的无边界使得在这样的链表上设计算法会比普通链表更加容易
对于新加入的节点应该是在第一个节点之前还是最后一个节点之后可以根据实际要求灵活处理,区别不大

栈的定义

栈(stack )又称堆栈,它是运算受限的线性表。其限制是仅允许在表的一端进行插入和删除操作,不允许在其他任何位置进行插入、查找、删除等操作
表中进行插入、删除操作的一端称为 栈顶(top),栈顶保存的元素称为栈顶元素。相对的,表的另一端称为栈底(bottom)

  • 当栈中没有数据元素时称为空栈;
  • 向一个栈插入元素又称为 进栈或 入栈 push;
  • 从一个栈中删除元素又称为 出栈或 退栈 pop。
  • 由于栈的插入和删除操作仅在栈顶进行,后进栈的元素必定先出栈,
  • 所以又把堆栈称为 先进后出表(Last In First Out,简称 LIFO)

队列

队列定义

队列(queue)简称队,它同堆栈一样,也是一种运算受限的线性表,其限制是仅允许在表的一端进行插入,而在表的另一端进行删除
在队列中把插入数据元素的一端称为 队尾(rear),删除数据元素的一端称为队首(front)
向队尾插入元素称为 进队或入队,新元素入队后成为新的队尾元素;从队列中删除元素称为离队或出队,元素出队后,其后续元素成为新的队首元素
由于队列的插入和删除操作分别在队尾和队首进行,每个元素必然按照进入的次序离队,也就是说先进队的元素必然先离队,所以称队列为 先进先出表(First In First Out,简称FIFO)

双端队列deque

double ended queue 通常读为"deck"

所谓双端队列是指两端都可以进行进队和出队操作的队列,如下图所示,将队列的两端分别称为前端和后端,两端都可以入队和出队,其元素的逻辑结构仍是线性结构
在双端队列进队时:前端进的元素排列在队列中后端进的元素的前面,后端进的元素排列在队列中前端进的元素的后面
在双端队列出队时,无论前端出还是后端出,先出的元素排列在后出的元素的前面。

在这里插入图片描述

输出受限的双端队列,即一个端点允许插入和删除,另一个端点只允许插入的双端队列
在这里插入图片描述

输入受限的双端队列,即一个端点允许插入和删除,另一个端点只允许删除的双端队列
在这里插入图片描述

双端队列既可以用来队列操作,也可以用来实现栈操作(只操作一端就是栈了)

树是由一个集合以及在该集合上定义的一种关系构成的。集合中的元素称为树的结点,所定义的关系称为父子关系。
父子关系在树的结点之间建立了一个层次结构。
树的结点包含一个数据元素及若干指向其子树的若干分支。
在这种层次结构中有一个结点具有特殊的地位,这个结点称为该树的根结点,或简称为树根

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

week@eight

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值