You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

14 KiB

title author top cover toc mathjax summary tags categories reprintPolicy abbrlink date coverImg img password
数据结构和算法-未完成 TianZD true true true false 基于Java的数据结构和算法,未完成,粗略学了一下,没有参考价值 [数据结构 算法] [java 数据结构和算法] cc_by b1f89a38 2022-04-29 10:42:56 <nil> <nil> <nil>

[TOC]

概述:学习《漫画算法:小灰的算法之旅》

1、数据结构基础

1.1、数据结构分类

数据结构是以某种特定的布局方式可存储数据的容器,布局方式决定了数据结构对于某些操作是高效的,对于其他操作是低效的。

数据结构 = 逻辑结构+物理结构(顺序、链式、索引、散列)

  • 逻辑结构

    • 线性结构:顺序表、栈、队列
    • 非线性结构:树、图
  • 物理结构:(存储结构),在计算机存储器中的存储形式

    • 顺序存储结构:数组
    • 链式存储结构:链表

逻辑分类:

  1. 线性结构:结构中的元素存在一对一的相互关系(元素之间有序,一个挨一个),常见的有:线性表、栈、队列、串(一维数组);
  2. 树形结构:元素存在一对多的相互关系;常见的有:二叉树、红黑树、B树、哈夫曼树等;
  3. 图形结构:元素存在多对多的关系;常见:有向图、无向图、简单图;

1.2、数组

概述

  • 有限个相同类型的变量组成的有序集合
  • 物理结构:顺序存储结构,在内存中顺序存储
  • 逻辑结构:线性结构

基本操作

读取元素

可以直接通过下标进行随机读取,时间复杂度O(1)

更新元素

通过下标直接进行赋值替换,时间复杂度O(1)

插入元素

涉及到元素的移动,时间复杂度O(n)

  • 尾部插入

  • 中间插入

  • 超范围插入(扩容)

删除元素

和插入相反,时间复杂度O(n)

优劣势

优点:

  • 数据访问:可以通过下标进行,效率高

缺点:

  • 插入删除:在内存中采用顺序存储,插入和删除导致大量元素移动,效率低

总结:适用于读操作多、写操作少的场景

1.3、链表

概述

  • 分为单向链表和双向链表
  • 物理上非连续、非顺序,在内存中随机存储
  • 由若干节点node组成
  • 物理结构:链式存储结构
  • 逻辑结构:线性结构

单向链表

每一个node包含两个属性:

  • data:存放数据的变量
  • node节点指针next:指向下一个节点
private static class Node{
    int data;
    Node next;
}

单向链表只能通过next指针找到关联的下一个节点,逐一寻找,只需要知道第一个节点,便可以根据找到所有的节点,因此用第一个节点标识链表,单向链表的第一个节点又称为头节点

头尾节点:

  • 头节点Head:链表的第一个节点
  • 尾节点:链表的最后一个节点,next指针指向空,null

双向链表

双向链表每一个node包含三个属性:

  • data
  • node节点指针next
  • node节点指针prev:指向前一个节点

基本操作

查找节点

从头节点开始,向后逐一查找,时间复杂度O(n)

更新节点

找到后直接将node节点数据进行替换,查找时间复杂度O(n),替换复杂度O(1)

插入节点

如果不考虑查找元素的过程,时间复杂度O(1)

  • 尾部插入

将尾节点的next指针指向新节点

  • 头部插入

1.把新节点的next指向原来的头节点

2.把新节点变为头节点

  • 中间插入

1.将新节点的next指向插入位置的节点

2.将插入位置前的节点的next指向新节点

删除元素

如果不考虑查找元素的过程,时间复杂度O(1)

  • 尾部删除

把倒数第二个node的next指向空

  • 头部删除

把链表的头节点设为原来的第二个节点

  • 中间删除

把要删除节点的前一个节点的next指向后一个节点

优劣势

优势:

  • 在于能够灵活地进行插入和删除操作

劣势:

  • 查找需要从头部开始逐一遍历

1.4、栈

概述

  • 逻辑结构:线性存储结构
  • 物理结构:可以用数组实现也可以用链表
  • 先入后出FILO
    • 栈顶:最后进入元素的位置
    • 栈底:最先进入元素的位置

基本操作

入栈push

把新元素放入栈中,只能从栈顶放入,作为新的栈顶

时间复杂度O(1)

出栈pop

把元素弹出,只能弹出栈顶元素

时间复杂度O(1)

应用

输出顺序和输入顺序相反,通常用于对“历史”的回溯

  • 代替递归
  • 面包屑导航(浏览页面)

1.5、队列

概述

  • 逻辑结构:线性存储结构
  • 物理结构:可以用数组实现也可以用链表
  • 先入先出FIFO
    • 对头front
    • 对尾rear

基本操作

入队enqueue

把新元素放入队尾

时间复杂度O(1)

出队dequeue

把对头元素移出队列

时间复杂度O(1)

循环队列

数组实现的队列为了防止出队时,对头左边的空间失去作用,可以采用循环队列的方式维持队列容量的恒定

  • 入队:
    • 队列满:(rear+1)%array.length==front
    • rear=(rear+1)%array.length;
  • 出队:
    • 队列空:rear==front
    • front=(front+1)%array.length;

应用

输出顺序和输入顺序相同,用于对历史的回放

  • 多线程中争夺锁
  • 网络爬虫抓取

1.6、散列表(哈希表)

概述

  • 散列表提供了键(key) 和**值(value)**的映射关系,可以根据key快速找到对应的value,时间复杂度接近O(1)
  • 本质上是数组
  • 通过哈希函数将key转换为数组中的下标

哈希函数

hashcode

在java等面向对象的语言中,每一个对象都有自己的hashcode,hashcode是区分不同对象的重要标识,不论对象的类型是什么,hashcode都是一个整型变量

转化

通常采用hashcode对数组长度的取模运算

index = HashCode(key) % Array.length

java中的哈希函数采用的是位运算的方式,将数组长度取为2的次方,提高性能。

基本操作

写操作

  1. 通过哈希函数,将key转化为数组下标
  2. 放入元素
    1. 如果对应的下标没有元素,直接放入
    2. 如果已有元素,产生哈希冲突

哈希冲突解决:

  • 开放寻址法:已有元素时,index++,放入下一个,直至对应的下标没有元素占用,ThreadLocal采用
  • 链表法HashMap采用,其数组中的每一个元素对应链表的一个节点,最先放入的为头节点,后续放入的通过将上一个节点的next指向该元素,插入到链表中

读操作

  1. 通过哈希函数,将key转化为数组下标
  2. 找到对应下标对应的元素
    1. 如果只有一个,则找到
    2. 如果对应的链表,遍历链表,直到key值对应

扩容

当多次插入后,key映射位置发生冲突的概率提高,大量元素拥挤在相同的位置,形成链表很长,导致查询和写入效率低下

条件:

HashMap.Size >= Capacity * LoadFactor

步骤

  • 扩容:新建一个空数组,长度为两倍
  • 重新Hash:数组长度变化,需要重新根据哈希函数计算哈希值

1.7、树和二叉树

概述

  • 逻辑结构:非线性结构

二叉树

二叉树应用

查找

维持相对顺序

二叉树遍历

从节点位置:

  • 前序遍历
  • 中序遍历
  • 后序遍历
  • 层序遍历

从宏观角度:

  • 深度优先遍历:前序、中序、后序
  • 广度优先遍历:层序

深度优先遍历

广度优先遍历

1.8、二叉堆

概述

  • 最大堆
  • 最小堆

自我调整

插入

删除

构建

代码

1.9、优先队列

概述

实现

2、排序算法

2.1、分类

2.2、冒泡排序

基本冒泡排序

交换排序,两两比较,两层循环,第一层为遍历的轮数:array.length-1

时间复杂度O(n^2)

优化冒泡排序

第一轮

防止数列已经有序后仍然进行下一轮排序

如{5,8,6,3,9,2,1,7}

第二轮

现有逻辑:从for(int j=0;j<array.length-1;i++)可以看出有序区的长度为以进行排序的轮数i

当后面大部分已经有序后,仍然会进行比较,对此进行优化

如{3,4,2,1,5,6,7,8}

鸡尾酒排序

双向交换,奇数轮从左向右,偶数轮从右向左

解决大部分已经有序的情况,如{2,3,4,5,6,7,8,1}

2.3、快速排序

概述

核心:分治思想

选定基准元素,大的放右边,小的放左边,一分为2

时间复杂度O(nlogn)

基准元素选择

  • 默认数列的第一个元素
  • 可以随机选择一个元素作为基准,然后和首元素交换位置

元素交换

双边循环法

  • 基准元素pivot
  • 左指针left
  • 右指针right

单边循环法

  • 基准元素pivot
  • 元素区域边界mark

递归、非递归实现

2.4、堆排序

2.5、计数排序和桶排序

2.6、排序算法复杂度分析

排序算法 平均时间复杂度 最坏时间复杂度 空间复杂度 是否稳定
冒泡排序 O(n^2) O(n^2) O(1) 稳定
鸡尾酒排序 O(n^2) O(n^2) O(1) 稳定
快速排序 O(nlogn) O(n^2) O(logn) 不稳定
堆排序 O(nlogn) O(nlogn) O(1) 不稳定
计数排序 O(n+m) O(n+m) O(m) 稳定
桶排序 O(n) O(nlogn) O(n) 稳定

3、面试算法

3.1、判断链表是否有环

基础版

问题

如何判断一个链表是否有环

思路

链表若有环,两个指针分别不同速度,速度快的会追上速度慢的

  • 两个指针指向头节点:Node p1 = head; Node p2 = head;
    • p1每次移动一个位置:p1=p1.next;
    • p2每次移动两个位置p2=p2.next.next;
  • p1和p2相遇则有环:p1==p2

代码

扩展

问题

若有环,求环的长度和入环节点

思路

环的长度:p1和p2的速度相差1,因此二次相遇之间走过的步数就是环的长度

入环节点:通过数学计算可以得到,入环点到头节点的距离==首次相遇点到入环点之间的距离,因此首次相遇后,把一个指针重新指向头节点,两个指针同时一次走一步,再次相遇的点就是入环节点

3.2、最小栈

问题

实现一个栈,有出栈、入栈,另外要有取最小值方法,同时三个方法的时间复杂度都为O(1)

思路

代码

3.3、求最大公约数

问题

求两个整数的最大公约数

思路

辗转相除法(欧几里得算法):两个正整数a和b(a>b)的最大公约数等于a除以b的余数c和b之间的最大公约数

更相减损法:两个正整数a和b(a>b)的最大公约数等于a-b的差值c和b之间的最大公约数

代码

优化

位运算

3.4、判断一个数是否是2的整数次幂

问题

判断一个数是否是2的整数次幂

思路

  • 2的整数次幂装换位二进制时,首位为1,其余位为0
  • 2的整数次幂减去1,转换为二进制时,全部为1,首位为0
  • 与运算&

代码

3.5、无序数组排序后的最大相邻差

问题

求出数组排序后的任意两个相邻元素的最大差值

思路

采用桶排序的思想,

  • 分桶
  • 入桶,记录每一个桶的最大值和最小值
  • 统计相邻两个桶的最大值和最小值的差

代码

3.6、用栈实现队列

问题

用栈模拟队列,实现:入队、出队

思路

  • 两个栈A和B
  • 入队在A中压栈
  • 出队
    • B是否为空,空的话首先将A中的元素弹出到B中
    • B弹栈

代码

3.7、寻找全排列的下一个数

问题

给一个正整数,找出全排列的下一个数

用这个数包含的全部数字重新排列,找出一个大于且仅大于原数的新整数

思路

固定的几个数字进行排列,逆序排列时数字最大,顺序排列时数字最小,同时为了保持重新排列的数仅比原数大,要尽可能保持高位不变,仅仅改变低位的数字

  • 从后找,找到当前整数的逆序区域(逆序区域已经是最大的了,要找到不是逆序区域的数字的位置),如12354,只变换5和4不能满足比原数大,要从3开始变
  • 更换找到的位置的数字和最末尾数字,变为12435
  • 把逆序区域变为顺序,即交换5和4,保持最小

代码

3.9、删除k个数字后的最小值

问题

给一个整数,删除k个数字,要求剩下的数字尽可能小

思路

删除掉一个数字后,整数的位数减少1

需要从高位起,若该位置数字比后一位大,则需要去掉

代码

3.9、实现大整数相加

问题

实现两个很大的整数相加

思路

根据手动相加思路,低位相加,大于10后,高位加1

  • 数字逆序存到两个数组中
  • 遍历,若两个数组对应的位置值相加大于10,则保留个位,且高位加1
  • 下一位相加,同时要加上低位进的1

代码

3.10、金矿问题

问题

10名工人,5堆金矿,价值{500,400,350,300,200},需要人力{5,5,3,4,3}

求解如何最大收益

思路

动态规划

代码

3.11、寻找缺失的整数

基本问题

问题

一个无序数组,有99个不重复的正整数,范围1-100,求出100中缺少的那个

思路

先计算累加和,然后减去有的99个数,得到的即为缺少的

扩展1

问题

数量若干,同时99个整数出现了偶数次,1个出现了奇数次,求出这一个数

思路

异或运算

扩展2

问题

2个数字出现了奇数次,求这两个数

思路

分治法

4、算法的应用

4.1、Bitmap

4.2、LRU

4.3、A星寻路算法

4.4、红包算法