基础知识--数据结构

注:本文是个人学习数据结构的知识整理,大部分参考其他链接内容(都已附原链接地址),仅供参考。

文章目录

数据结构

1.线性表

线性表(List):零个或多个元素组成的有序序列。

在这里插入图片描述

1.1 数组列表-线性表的顺序存储结构

线性表的顺序存储结构:是指用一段连续的地址单元存放线性表的元素。

1.1.1 定义

在这里插入图片描述
在这里插入图片描述

1.1.2 插入操作

在这里插入图片描述
时间复杂度O(N)

1.1.3 删除操作

在这里插入图片描述
时间复杂度O(N)

1.1.4 优缺点

在这里插入图片描述

1.1.5 Java实现类:ArrayList

创建:List list = new ArrayList<>();

1.2 链表-线性表的链式存储结构

线性表的链式存储结构:使用一组任意的存储单元存储线性表的元素。

1.2.1 定义

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.2.2 单链表的读取

时间复杂度O(N)

1.2.3 单链表的插入和删除

插入:
在这里插入图片描述
删除:
在这里插入图片描述
时间复杂度都是O(1)

1.2.4 顺序、链式存储结构对比

在这里插入图片描述

1.2.5 Java实现类:LinkedList

创建:List list = new LinkedList<>();

1.2.6 静态链表

定义:使用数组实现的链表。每个元素包含数据域data和后继元素在数组中的坐标位置。
在这里插入图片描述

1.2.7 循环链表

定义:
在这里插入图片描述
在这里插入图片描述

1.2.8 双向链表

定义:链表节点不仅有指向后继节点的指针,还存在指向钱去节点的指针。

在这里插入图片描述

1.2.9 链表常考问题

统一解题思路:快慢指针

1、判断单链表的长度

从头结点向尾节点开始遍历,直到结束,记录遍历的元素个数。

2、判断单链表是否存在环

快慢指针,起始位置慢指针在头结点,快指针在头结点的next节点,慢指针每次移动1步,快指针每次移动2步。每次移动后,两指针距离+1。

如果存在环,两指针终究因为“套圈”而相遇。

3、判断单链表环的长度

快慢指针,两指针相遇后,开始记录移动的次数,直到再次相遇。

移动的次数即为环的长度。

4、取指定位置的元素:第k个元素、倒数第k个元素、中间位置的元素等

第k个元素:从头节点开始遍历,直到第k个节点。

倒数第k个元素:快慢指针

起始位置,快慢指针都在头结点。

快指针首先移动k步,之后每次移动两指针均向后移动一步,直到快指针遍历链表结束。

此时,慢指针所在的结点为倒数第k个结点。

中间位置的元素:快慢指针

设链表长度为N。

起始位置,快慢指针都在头结点。

每次移动,快指针移动2步,慢指针移动1步,直到快指针遍历结束。则最多移动N/2次。

此时,如果链表长度N为奇数,慢指针正好指向中间结点;如果N为偶数,慢指针指向中间两个结点的靠前一个。

2. 栈与队列

2.1 栈

2.1.1 概念解析

栈(Stack):限定仅可以在表尾进行插入和删除操作的线性表
栈顶(Top):允许插入和删除的一端称为栈顶。
栈底(Bottom):另一端称为栈底。
进栈、压栈、入栈:栈的插入操作。
出栈、弹栈:栈的删除操作。
在这里插入图片描述

2.1.2 特性

后进先出(Last In First Out)

2.1.3 栈的顺序存储结构

2.1.4 栈的链式存储结构

将栈顶放在单链表的头部
在这里插入图片描述
栈的数据结构代码:

在这里插入图片描述
进栈操作:
在这里插入图片描述
在这里插入图片描述
出栈操作:
在这里插入图片描述

2.1.5 栈的作用

总结:用于实现递归

1、问题背景:斐波那契数列实现
在这里插入图片描述
2、递归&迭代概念区分

迭代:使用循环程序结构,达成递归函数的效果。迭代不需要反复调用函数和占用额外的内存。

递归:一个函数直接或间接调用自己,被称作递归函数。递归函数必须至少有一个条件,满足时递归不再进行,即不再引用自身而是返回值退出。

递归的优点:代码清晰简洁。

递归的缺点:大量的递归调用会建立函数的副本,会耗费大量的时间和内存。

3、栈用于实现递归

在系统编译器中,递归的调用是通过栈来实现的。在函数的前行调用阶段,对于每一层递归,函数的局部变量、参数值以及返回地址将都被压入栈中。退回阶段,位于栈顶的局部变量、参数值和返回地址被弹出,用于恢复到调用前的状态。

2.1.6 栈的应用

1、后缀(逆波兰)表达式的计算

定义:一种不需要括号的后缀表示法,所有的符号都是在运算数字的后面出现。

2.1.7 Java实现类

1、Stack类

该类在jdk中的解释:
在这里插入图片描述
主要Api有:
创建:
入栈:
出栈:
栈顶元素:
检查是否空:
查找元素:
查看距离栈顶的位置:

2、Deque类

定义:允许在队头、队尾,都具有删除、添加元素操作的队列,叫做双端队列。

创建:Deque stack = new ArrayDeque();

常用Api:(以下内容取自Java数据结构之Deque(双端队列))

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.2 队列

2.2.1 概念解析

定义:队列(Queue)是一种只允许在一端进行插入操作(队尾),而另一端只允许进行删除操作(队头)的数据结构。

2.2.2 特性

先入先出(First In First Out,FIFO)
在这里插入图片描述

2.2.3 队列的顺序存储结构

2.2.4 队列的链式存储结构

在这里插入图片描述
在这里插入图片描述
入队操作:
在这里插入图片描述
在这里插入图片描述

出队操作:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.2.5 Java实现类

队列常见类的分类:
在这里插入图片描述

3. 串

3.1 定义

串是由零个或多个字符组成的有限序列,又叫字符串。
字符数目称为串的长度。

3.2 字符串的比较

在这里插入图片描述

3.3 串的存储结构

3.3.1 串的顺序存储结构

串的顺序存储结构是用一组地址连续的存储单元来存储串中的字符序列的。按照预定义的大小,为每个定义的感变量分配一个固定长度的存储区。一般是用定长数组来定义。

3.3.2 串的链式存储结构

可以一个结点存储一个字符,也可以为了节省内存资源,一个结点存储多个连续的字符。

3.4 串的朴素模式匹配

在这里插入图片描述

3.5 KMP模式匹配算法

参考:https://blog.csdn.net/weixin_43717520/article/details/115592947

4. 树

4.1 定义

4.1.1 树的定义

定义:是N个结点的有限集。
空树:N等于0;
非空树:有且仅有一个根结点;当N>1时,其余结点可以分为m个互不相交的子树。
在这里插入图片描述
树的深度或高度:树中结点的最大层次称为树的深度。
有序树:树中结点的各子树是有次序的,不能互换的。
无序树:树中结点的子树无次序,可以互换的。
森林:m(m >= 0)棵互不相交的树的集合。

4.1.2 结点定义

定义:树的结点包含一个数据元素及若干指向其子树的分支。
结点的度:结点拥有的子树的数目。
叶结点(终端结点):度为0的结点。
非终端结点/分支结点/内部结点:度不为0的结点。
树的度:树内各结点的度的最大值。
在这里插入图片描述
结点间的关系
孩子结点:结点子树的根节点。
双亲结点:该结点成为孩子结点的双亲结点。
兄弟结点:同一个双亲的孩子结点之间成为兄弟结点。
结点的祖先:从根节点到该结点所经分支上的所有结点。
结点的子孙:以某结点为跟结点的子树中任一结点都称为该结点的子孙结点。

结点的层次
在这里插入图片描述

4.1.3 树的抽象数据类型

在这里插入图片描述

4.1.4 树的存储结构

1、双亲表示法
在每个结点中,附设一个指针指向其双亲结点的位置。
在这里插入图片描述
2、孩子表示法
方法一:
结点存储其每个孩子结点的位置。
在这里插入图片描述
方法二:
在这里插入图片描述
把每个结点的孩子结点排列起来,以单链表作为存储结构,则n个结点有n个孩子链表。如果是叶子结点,则此单链表为空。然后n个头指针组成一个线性表,采用顺序存储结构,存放进一个一维数组中。

在这里插入图片描述
在这里插入图片描述
3、孩子双亲表示法
能够保证快速找到某结点的双亲结点
在这里插入图片描述
4、孩子兄弟表示法

设置两个指针,分别指向该结点的第一个孩子和此节点的右兄弟。
在这里插入图片描述
在这里插入图片描述
此种方法将一棵复杂的树变为了二叉树:
在这里插入图片描述

4.2 二叉树

4.2.1 二叉树定义

是n(n>=0)个结点的有限集合。该集合或者为空集(空二叉树),或者由一个根节点和两棵互不相交的、分别称为根节点的左子树和右子树的二叉树组成。
在这里插入图片描述

4.2.2 二叉树的特点

1、每个结点最多有两棵子树;
2、左子树和右子树是有顺序的,不可以互换顺序。
3、即使树中某结点只有一棵子树,也要区分是左子树还是右子树。

4.2.3 特殊二叉树

1、斜树
所有结点都同时只有左子树,或都同时只有右子树,称为斜树。相对应的叫做左斜树和右斜树。
2、满二叉树
在一棵二叉树中,如果所有结点都存在左子树和右子树,并且所有叶子都在同一层上
在这里插入图片描述

3、完全二叉树
对一棵具有n个结点的二叉树按层序编号,如果编号为i的结点与同样深度的满二叉树中编号为i的结点所在位置完全相同,则称为完全二叉树。
在这里插入图片描述
满二叉树是完全二叉树,反之不一定。

4.2.4 二叉树性质

1、在二叉树的第i层上至多有2^(i-1)个结点。
2、深度为k的二叉树至多有2^(k)-1个结点。
3、对任何一棵二叉树T,如果起终端结点数目为n0,度为2的结点数目为n2,则n0=n2+1。
4、具有n个结点的完全二叉树的深度为[log2(n)] + 1。([x]表示不大于x的最大整数)
5、
在这里插入图片描述

4.2.5 二叉树的存储结构

4.2.5.1 顺序存储结构

在这里插入图片描述在这里插入图片描述

4.2.5.2 链式存储结构

为每个结点设计一个数据域和两个指针域,被称为二叉链表。
在这里插入图片描述
在这里插入图片描述

4.2.6 二叉树的遍历

是指从根节点出发,按照某种次序依次访问二叉树中所有结点,是的每个结点被访问一次且仅被访问一次。
二叉树遍历算法的实现一般都是通过递归方法实现的

4.2.6.1 深度优先-前序遍历

先访问根结点,然后前序遍历左子树,再前序遍历右子树。

public void preOrder(TreeNode root) {
        if (root == null) {
            return;
        }
        Deque<TreeNode> stack = new LinkedList<>();
        List<Integer> list = new ArrayList<>();
        TreeNode node = root;
        while(node!= null || !stack.isEmpty()) {
            while (node != null) {
                stack.push(node);
                list.add(node.val); // 前序
                node = node.left;
            }
            node = stack.pop();
            node = node.right;
        }
        System.out.println(Arrays.toString(list.toArray()));
    }
4.2.6.2 深度优先-中序遍历

先中序遍历左子树,然后访问根结点,最后中序遍历右子树。

public void inOrder(TreeNode root) {
        if (root == null) {
            return;
        }
        Deque<TreeNode> stack = new LinkedList<>();
        List<Integer> list = new ArrayList<>();
        TreeNode node = root;
        while(node!= null || !stack.isEmpty()) {
            while (node != null) {
                stack.push(node);
                node = node.left;
            }
            node = stack.pop();
            list.add(node.val); // 中序
            node = node.right;
        }
        System.out.println(Arrays.toString(list.toArray()));
    }
4.2.6.3 深度优先-后序遍历

先后续遍历左子树,然后后续遍历右子树,最后访问根结点。

4.2.6.4 广度优先遍历(层序遍历)

从上而下逐层遍历,每一层中从左到右的顺序逐个访问结点。
使用队列实现。

public void levelOrder(TreeNode tree) {
        if (tree == null)
            return;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(tree);//相当于把数据加入到队列尾部
        while (!queue.isEmpty()) {
            //poll方法相当于移除队列头部的元素
            TreeNode node = queue.poll();
            System.out.println(node.val);
            if (node.left != null)
                queue.add(node.left);
            if (node.right != null)
                queue.add(node.right);
        }
    }
4.2.6.5 二叉树题目解法总结

1、DFS遍历
深度优先遍历(前序、中序、后序):

  • 一般通过递归实现;
  • 也可以栈+迭代实现。

经典例题:展开为链表

题号题目
114二叉树展开为链表
173二叉搜索树迭代器

DFS遍历的题目一般有两种解法思路:
(1) 自上而下:通过父结点的问题状态求解子问题的解,需要将父结点的信息通过参数传递给子结点。
经典例题:

题号题目
112路径总和
129求根节点到叶节点数字之和

(2) 自下而上:将父结点问题转化成求子结点或者子树问题的解,需要找到父问题和子问题解之间的关系。
经典例题:

题号题目
104二叉树的最大深度
100相同的树
226翻转二叉树
101对称二叉树
124二叉树中的最大路径和
236二叉树的最近公共祖先

2、BFS遍历
广度优先遍历(层序遍历):队列+迭代实现;
经典例题:

题号题目
119填充每个节点的下一个右侧节点指针 II

4.2.7 二叉树的建立

经典例题:

题号题目
105从前序与中序遍历序列构造二叉树
106从中序与后序遍历序列构造二叉树

4.3 线索二叉树

背景:普通二叉树的实现方法中,对于叶子结点,会有很多空指针域(叶子结点没有左子节点,也没有右子节点),内存利用率低。
方法:利用这些空地址,存放指向结点在某种遍历次序下的前驱和后继结点的地址。
定义:这种指向前驱或者后继结点的指针称为线索,加上线索的二叉链表称为线索链表,相应的二叉树被称为线索二叉树。
在这里插入图片描述
在这里插入图片描述
那如何确定某结点的lchild域存放的是左子结点还是前驱结点呢?增加tag域标识。
在这里插入图片描述

4.4二叉查找树(二叉排序树、二叉搜索树)

查找、插入结点、删除结点
在这里插入图片描述
在这里插入图片描述

4.5平衡二叉树(AVL树)

是一种二叉排序树,其中每一个节点的左子树和右子树的高度差最多等于1,是由两位俄罗斯数学家 G.M.Adelson-Ve]skii 和 E.M.Lanrus 在1962年发明,因此又被称为AVL树。我们将二叉树上结点的左子树深度减去右子树深度的值称为平衡因子 BF ,则平衡二叉树所有节点的平衡因子只可能是0,-1,1,只要二叉树上有一个节点的平衡因子绝对值大于1,则就不是平衡二叉树。
距离插入节点最近的,且平衡因子的绝对值大于1的结点为根的子树,我们称之为最小不平衡子树
实现原理:平衡二叉树构建的基本思想就是在构建二叉排序树的过程中,每当插入一个结点时,先检查是否因插入而破坏了树的平衡性,若是,则找出最小不平衡子树。 在保持二叉排序树特性的前提下,调整最小不平衡子树中各结点之间的链接关系,进行相应的旋转,使之成为新的平衡子树。
所谓的平衡二叉树,其实就是在二叉排序树创建过程中保证根的平衡性,当最小不平衡子树根结点的平衡因子 BF 是大于 1 时,就右旋,小于一 1 时就左旋,插入结点后,最小不平衡子树的 BF 与它的子树的 BF 符号相反时 ,就需要对结点先进行一次旋转以使得符号相同后,再反向旋转一次才能够完成平衡操作。
平衡二叉树的实现原理和算法实现,参考《大话数据结构》8.7.1节。

4.6红黑树

红黑树是平衡二叉树的一种,它保证在最坏情况下基本动态集合操作的时间复杂度为O(log n)。红黑树和平衡二叉树区别如下:(1) 红黑树放弃了追求完全平衡,追求大致平衡,在与平衡二叉树的时间复杂度相差不大的情况下,保证每次插入最多只需要三次旋转就能达到平衡,实现起来也更为简单。(2) 平衡二叉树追求绝对平衡,条件比较苛刻,实现起来比较麻烦,每次插入新节点之后需要旋转的次数不能预知。

4.7 树、森林和二叉树的转换

4.8 霍夫曼树及其应用

4.8.1 概念和定义

在这里插入图片描述
从树中一个结点到另一个结点的分支构成两个结点之间的路径,路径上的分支数目叫路径长度。
树的路径长度为根结点到每一个结点的路径长度之和。
考虑到带权重的结点,结点的带权路径长度为根结点到该结点的路径长度与权重的乘积。
树的带权路径长度为所有叶子结点带权路径长度之和。
带权路径长度最小的二叉树为最优二叉树,又叫作霍夫曼树。

构造霍夫曼树的方法:
在这里插入图片描述

4.8.2 霍夫曼编码

背景及目的
为了解决远距离通信中,根据通信内容中不同字符出现概率的不同,合理的规划每个字符的01编码规则,使得编码总长度达到最小,从而降低通信数据量。
编码方法

在这里插入图片描述
在这里插入图片描述

5.图

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值