Java 数据结构与算法

数据结构和算法的介绍

数据结构的介绍

数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率。数据结构往往同高效的检索算法索引技术有关。

数据结构和算法的关系
  1. 数据结构是基于计算机存储、组织数据的方式的学科;
  2. 程序 = 数据结构 + 算法;
  3. 数据结构是算法的基础;
实际编程中会遇到的问题
  1. 问题1:
    Java代码

    public static void main(String[] args){
    	String str = "Java,Java,hello,world!";
    	String newStr = str.replaceAll("Java","Test");
    	System.out.println("newStr=" + newStr);
    }
    

    问题:使用单链表表示的字符串及字符串节点类的定义,并依次实现它的构造函数、以及计算串长度、串赋值、判断两串相等、求字串、两串相连、求子串在串中位置等7各成员函数。

  2. 问题2
    Josephu 问题:设编号为1、2、. . . n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列,他的下一位又从1开始报数,数到m的那个人出列,以此类推,直到所有人出列为止,由此产生一个出队编号的序列。
    提示:
    用一个不带头节点的循环链表来处理Josephu问题:先构成一个有n个节点的单循环链表,人后由k节点起从1开始计数,计到m时,对应节点从链表中删除,然后再从被删除节点的下一节点开始从1计数,直到最后一个节点从链表中删除算法结束。

线性结果和非线性结构

数据结构包括:线性结构、非线性结构

  • 线性结构
    • 常用的数据结构,特点时数据元素之间存在一对一的线性关系
    • 两种存储结构:顺序存储结构、链式存储结构
      • 顺序存储结构:称为顺序表,存储的元素是连续的
      • 链式存储结构:称为链表,存储的元素不一定连续,元素节点中存放数据元素以及相邻元素的地址信息
    • 常见线性结构:数组、队列、链表、栈
  • 非线性结构
    • 常见非线性结构:二维数据、多维数组、广义表、树结构、图结构

稀疏数组和队列

一个实际场景

实际问题
编写的五子棋程序中,有存盘继续上盘的功能。
在这里插入图片描述
分析问题
该二维数组的很多值是默认值0,因此记录了很多没有意义的数据,可以通过稀疏数组进行压缩,进而优化。

稀疏 sparsearray 数组
  • 基本介绍:
    • 当一个数组中大部分元素为0或者为同一个值的数组时,可以使用稀疏数组来保存该数组。
  • 稀疏数组的处理方式:
    • 记录数组一共有几行几列,有多少个不同的值
    • 把具有不同值的元素的行列及值记录在一个小规模的数组中,从而缩小程序的规模

在这里插入图片描述

行(row)列(col)值(value)
[0]678
[1]0322
[2]0615
[3]1111
[4]1517
[5]23-6
[6]3539
[7]4091
[8]5228

[0] 记录的时数组的整体情况,row记录一共多上行,col记录一共多少列,value记录非0的值的个数。
[1]~[n] 记录每一行的非0值(有效数据)的位置,及其值的大小。

结果:数据体量从原来的 6 * 7=42 压缩到 9 * 3=27。

实现数组的压缩和解压

应用实例

  1. 使用稀疏数组,保留上述的二维数组(棋盘、地图等)
  2. 把稀疏数组存盘,并且可以恢复到原来的二维数组

二维数组转稀疏数组思路

  1. 遍历原始二维数组,得到有效数据的个数
  2. 根据sum就可以创建稀疏数组sparseArr int[sum + 1] [3]
  3. 构建稀疏组数的第一行
  4. 将二维数组的有效数据存入到稀疏数组

稀疏数组转二维数组思路

  1. 先读取稀疏数组的第一行,根据第一行的数据,创建原始额二维数组
  2. 再读取稀疏数组后面的数据,赋给原始数组

示例实现

public class SparseArray {

    public static void main(String[] args){
        // 创建一个原始的二维数组 11 * 11
        // 0:表示没有棋子,1:黑子,2:白子
        int chessArr1[][] = new int[11][11];
        chessArr1[1][2] = 1;
        chessArr1[2][3] = 2;
        // 输出原始的二维数组
        for(int[] row : chessArr1){
            for(int data : row){
                System.out.printf("%d\t", data);
            }
            System.out.println();
        }
        System.out.println("---------------------------------------------------------");

        // 将二维数组转稀疏数组
        // 1、先遍历二维数组得到非0数据的个数
        int sum = 0;
        for(int i = 0; i <11; i++){
            for(int j = 0; j < 11; j++){
                if(chessArr1[i][j] != 0){
                    sum++;
                }
            }
        }

        // 2、创建对应的稀疏数组
        int sparseArr[][] = new int[sum + 1][3];
        // 给稀疏数组赋值
        sparseArr[0][0] = 11;
        sparseArr[0][1] = 11;
        sparseArr[0][2] = sum;

        // 遍历二维数组,将非0得值存放到 sparseArr中
        int count = 0; // 记录是第几个非0数据
        for(int i = 0; i <11; i++){
            for(int j = 0; j < 11; j++){
                if(chessArr1[i][j] != 0){
                    count++;
                    sparseArr[count][0] = i;
                    sparseArr[count][1] = j;
                    sparseArr[count][2] = chessArr1[i][j];
                }
            }
        }

        // 输出稀疏数组得形式
        for(int i=0; i<sparseArr.length; i++){
            System.out.printf("%d\t%d\t%d\t\n", sparseArr[i][0], sparseArr[i][1], sparseArr[i][2]);
        }
        System.out.println("---------------------------------------------------------");

        // 将稀疏数组转二维数组
        // 1、先读取稀疏数组的第一行,根据第一行的数据,创建原始额二维数组
        int chessArr2[][]  = new int[sparseArr[0][0]][sparseArr[0][1]];

        // 2、再读取稀疏数组后面的数据,赋给原始数组
        for(int i=1; i<sparseArr.length; i++){
            chessArr2[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2];
        }

        // 输出恢复的二维数组
        for(int[] row : chessArr2){
            for(int data : row){
                System.out.printf("%d\t", data);
            }
            System.out.println();
        }
    }
}

队列
队列的一个使用场景

银行排队:领号排队,然后根据号码在到柜台办理业务的过程其实就是一个队列的场景。

队列介绍
  • 队列是一个有序列表,可以用数组或是链表来实现。
  • 遵循先进先出的原则。即:先存入列表的数据,要先取出。后存入的要后取出。
  • 示意图:(使用数组模拟队列示意图)
    在这里插入图片描述
数组模拟队列分析和实现

分析

  • 队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如上图,其中 maxSize 是该队列的最大容量。
  • 因为队列的输出、输入是分别从前后端来处理,因此需要两个变量 frontrear 分别记录队列前后端的下标,front 会随着数据输出而改变,而 rear 则是随着数据输入而改变。
  • 当将数据存入队列 addQueue,实际处理分为两步:
    • 将尾指针往后移:rear+1,当 front = rear 时,队列为空;
    • 若尾指针 rear 小于队列的最大下标 maxSize-1 时,则将数据存入 rear 所指的数组元素中,否则无法存入数据。rear == maxSize-1 时队列就满了。

实现

数组模拟环形队列分析和实现

链表

链表(Linked List)介绍
单链表
单链表介绍
单链表增删改查、包括顺序插入
单链表面试题
双向链表
双向链表介绍
双向链表的增删改查
单向环形链表
单向环形链表应用实例
Josephu 问题
单向环形链表解决约瑟夫问题

栈的一个实际需求
请输入一个表达式提出问题
栈的介绍
栈的应用场景
栈的快速入门
数组模拟栈分析和实现
栈实现综合计算器
完成表达式的计算-使用栈
前缀、中缀、后缀表达式(逆波兰表达式)
前缀表达式(波兰表达式)
前缀表达式的计算机求值
中缀表达式
后缀表达式
后缀表达式的计算机求值
逆波兰计算器
完成一个逆波兰计算器,分析和代码实现
中缀表达式转换为后缀表达式
中缀表达式转后缀表达式步骤
举例说明(图解)
代码实现中缀表达式转为后缀表达式
逆波兰计算器完整版
完整版逆波兰计算器,功能包括
  1. 支持 + - * / ()
  2. 多位数,支持小数
  3. 兼容处理,过滤任何空白字符,包括空格、制表符、换页符

递归

递归应用场景
递归的概念
递归调用机制
打印问题
阶乘问题
递归能解决的问题
递归遵循的重要原则
迷宫问题
迷宫问题分析
迷宫问题代码分析和图解
测试正确性
八皇后问题(回溯算法)
八皇后问题介绍
八皇后问题算法思路分析和图解
八皇后问题算法代码实现
八皇后游戏测试算法正确性

排序算法

排序算法的介绍
内部排序(使用内存)
插入排序
希尔排序
选择排序
堆排序
冒泡排序
快速排序
归并排序
基数排序(升级版桶排序)
外部排序(使用内存和外存结合)
算法的时间复杂度
度量一个程序执行时间的两种方法
时间频度
时间复杂度
常见的时间复杂度分别举例和分析

常数阶 O(1)
对数阶 O(log2n)
线性阶 O(n)
线性对数阶 O(nlog2n)
平方阶 O(n^2)
立方阶 O(n^3)
k次方阶 O(n^k)
指数阶 O(2^n)

平均时间复杂度和最坏时间复杂度
算法的空间复杂度简介
冒泡排序
基本介绍
算法分析图解
代码实现冒泡算法
选择排序
基本介绍
算法分析图解
代码实现选择算法
插入排序
基本介绍
算法分析图解
代码实现插入算法
希尔排序
基本介绍
算法分析图解
代码实现希尔算法
快速排序
基本介绍
算法分析图解
代码实现快速算法
归并排序
基本介绍
算法分析图解
代码实现归并算法
基数排序(桶排序)
基本介绍
算法分析图解
代码实现基数算法
常用排序算法总结和对比

查找算法

查找算法介绍
线性查找
基本介绍
算法分析图解
代码实现线性查找算法
二分查找
基本介绍
算法分析图解
代码实现线二分查找算法
插值查找
基本介绍
算法分析图解
代码实现插值查找算法
斐波那契(黄金分割)查找
基本介绍
算法分析图解
代码实现斐波那契(黄金分割)查找算法

哈希表

哈希表的基本原理
哈希表(散列)-Google上机题
实现哈希表[数组+链表]
  1. 哈希表创建
  2. 哈希表的添加
  3. 哈希表的查找

数结构基础部分

二叉树
树结构-非线性结构的需求
二叉树示意图
二叉树的概念
二叉树遍历的说明
二叉树遍历应用实例(前序、中序、后序)
二叉树-查找指定节点
二叉树-删除节点
顺序存储二叉树
顺序存储二叉树的概念和特点
顺序存储二叉树遍历
线索化二叉树
实际问题引入-线索化二叉树
线索化二叉树基本介绍
线索化二叉树分析图解
线索化二叉树代码实现
遍历线索化二叉树

树结构实际应用

堆排序
堆排序基本介绍
堆排序基本思想
堆排序步骤图解说明
堆排序代码实现
赫夫曼树
基本介绍
赫夫曼树几个重要概念和举例说明
赫夫曼树的代码实现
赫夫曼编码
基本介绍
原理剖析及其图解
最佳实践-数据压缩(创建赫夫曼树)
最佳实践-数据压缩(生产赫夫曼编码和编码后的数据)
最佳实践-数据解压(使用赫夫曼编码解码)
最佳实践-文件压缩
最佳实践-文件解压
赫夫曼编码压缩文件注意事项
二叉排序树
实际问题引入-二叉排序树
解决方案分析
二叉排序树介绍
二叉排序树创建和遍历
二叉排序树的删除
平衡二叉树(AVL树)
二叉排序树可能的问题
基本介绍
应用案例-单旋转(左选择)
应用案例-单旋转(右选择)
应用案例-双旋转

多路查找树

二叉树与B树
二叉树的问题分析
多叉树
B树的基本介绍
2-3树
2-3树基本介绍
2-3树应用案例
2-3树其他说明
B树、B+树和B*树
B树的介绍
B树的运行原理
B+树的介绍和原理
B*树的介绍和原理

图基本介绍
图的常用概念
图的快速入门案例
图的深度优化搜索算法介绍
图的创建和深度优化搜索算法

常用的10大算法

二分查找算法(非递归)
二分查找算法(非递归)介绍
二分查找算法(非递归)分析图解
二分查找算法(非递归)代码实现
分治算法
分治算法介绍
分治算法的基本步骤
分治(Divide-and-Conquer§)算法设计模式
分治算法最佳实践-汉诺塔实现
动态规划算法
应用场景-背包问题
动态规划算法介绍
动态规划算法最佳实践-背包问题分析和实现
KMP算法
应用场景-字符串匹配问题
暴力匹配算法
KMP算法介绍
搜索词和部分匹配值
KMP算法最佳应用-字符串匹配问题分析和实现
贪心算法
应用场景-集合覆盖问题
贪心算法介绍
贪心算法最佳应用-集合覆盖的分析和实现
贪心算法注意事项和细节
普里姆算法
应用场景-修路问题
最小生成树(Minimum Cost Spanning Tree)
最小连通子图
普里姆算法介绍
普里姆算法最佳实践(修路问题)分析和实现
克鲁斯卡尔算法
应用场景-公交站问题
克鲁斯卡尔(kruskal)算法介绍
加权连通图的最小生成树的算法
克鲁斯卡尔算法图解说明
克鲁斯卡尔算法代码实现
迪杰斯特拉算法
应用场景-最短路径问题
迪杰斯特拉(Dijkstra)算法介绍
广度有限搜索思想
迪杰斯特拉(Dijkstra)算法过程
迪杰斯特拉(Dijkstra)算法最佳应用-最短路径分析和实现
弗洛伊德算法
弗洛伊德(Floyed)算法介绍
弗洛伊德算法 VS 迪杰斯特拉算法
弗洛伊德算法(Floyed)算法图解分析
弗洛伊德算法(Floyed)算法最佳应用-最短路径分析和实现
马踏棋盘算法
马踏棋盘算法介绍和游戏演示
马踏棋盘游戏分析和代码实现
简单回溯法解决马踏棋盘的问题
深度优先搜索+贪心算法优化
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值