数据结构和算法的介绍
数据结构的介绍
数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率。数据结构往往同高效的检索算法和索引技术有关。
数据结构和算法的关系
- 数据结构是基于计算机存储、组织数据的方式的学科;
- 程序 = 数据结构 + 算法;
- 数据结构是算法的基础;
实际编程中会遇到的问题
-
问题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
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] | 6 | 7 | 8 |
[1] | 0 | 3 | 22 |
[2] | 0 | 6 | 15 |
[3] | 1 | 1 | 11 |
[4] | 1 | 5 | 17 |
[5] | 2 | 3 | -6 |
[6] | 3 | 5 | 39 |
[7] | 4 | 0 | 91 |
[8] | 5 | 2 | 28 |
[0] 记录的时数组的整体情况,row记录一共多上行,col记录一共多少列,value记录非0的值的个数。
[1]~[n] 记录每一行的非0值(有效数据)的位置,及其值的大小。
结果:数据体量从原来的 6 * 7=42 压缩到 9 * 3=27。
实现数组的压缩和解压
应用实例:
- 使用稀疏数组,保留上述的二维数组(棋盘、地图等)
- 把稀疏数组存盘,并且可以恢复到原来的二维数组
二维数组转稀疏数组思路:
- 遍历原始二维数组,得到有效数据的个数
- 根据sum就可以创建稀疏数组sparseArr int[sum + 1] [3]
- 构建稀疏组数的第一行
- 将二维数组的有效数据存入到稀疏数组
稀疏数组转二维数组思路:
- 先读取稀疏数组的第一行,根据第一行的数据,创建原始额二维数组
- 再读取稀疏数组后面的数据,赋给原始数组
示例实现
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 是该队列的最大容量。
- 因为队列的输出、输入是分别从前后端来处理,因此需要两个变量 front 及 rear 分别记录队列前后端的下标,front 会随着数据输出而改变,而 rear 则是随着数据输入而改变。
- 当将数据存入队列 addQueue,实际处理分为两步:
- 将尾指针往后移:rear+1,当 front = rear 时,队列为空;
- 若尾指针 rear 小于队列的最大下标 maxSize-1 时,则将数据存入 rear 所指的数组元素中,否则无法存入数据。rear == maxSize-1 时队列就满了。
实现:
数组模拟环形队列分析和实现
链表
链表(Linked List)介绍
单链表
单链表介绍
单链表增删改查、包括顺序插入
单链表面试题
双向链表
双向链表介绍
双向链表的增删改查
单向环形链表
单向环形链表应用实例
Josephu 问题
单向环形链表解决约瑟夫问题
栈
栈的一个实际需求
请输入一个表达式提出问题
栈的介绍
栈的应用场景
栈的快速入门
数组模拟栈分析和实现
栈实现综合计算器
完成表达式的计算-使用栈
前缀、中缀、后缀表达式(逆波兰表达式)
前缀表达式(波兰表达式)
前缀表达式的计算机求值
中缀表达式
后缀表达式
后缀表达式的计算机求值
逆波兰计算器
完成一个逆波兰计算器,分析和代码实现
中缀表达式转换为后缀表达式
中缀表达式转后缀表达式步骤
举例说明(图解)
代码实现中缀表达式转为后缀表达式
逆波兰计算器完整版
完整版逆波兰计算器,功能包括
- 支持 + - * / ()
- 多位数,支持小数
- 兼容处理,过滤任何空白字符,包括空格、制表符、换页符
递归
递归应用场景
递归的概念
递归调用机制
打印问题
阶乘问题
递归能解决的问题
递归遵循的重要原则
迷宫问题
迷宫问题分析
迷宫问题代码分析和图解
测试正确性
八皇后问题(回溯算法)
八皇后问题介绍
八皇后问题算法思路分析和图解
八皇后问题算法代码实现
八皇后游戏测试算法正确性
排序算法
排序算法的介绍
内部排序(使用内存)
插入排序
希尔排序
选择排序
堆排序
冒泡排序
快速排序
归并排序
基数排序(升级版桶排序)
外部排序(使用内存和外存结合)
算法的时间复杂度
度量一个程序执行时间的两种方法
时间频度
时间复杂度
常见的时间复杂度分别举例和分析
常数阶 O(1)
对数阶 O(log2n)
线性阶 O(n)
线性对数阶 O(nlog2n)
平方阶 O(n^2)
立方阶 O(n^3)
k次方阶 O(n^k)
指数阶 O(2^n)
平均时间复杂度和最坏时间复杂度
算法的空间复杂度简介
冒泡排序
基本介绍
算法分析图解
代码实现冒泡算法
选择排序
基本介绍
算法分析图解
代码实现选择算法
插入排序
基本介绍
算法分析图解
代码实现插入算法
希尔排序
基本介绍
算法分析图解
代码实现希尔算法
快速排序
基本介绍
算法分析图解
代码实现快速算法
归并排序
基本介绍
算法分析图解
代码实现归并算法
基数排序(桶排序)
基本介绍
算法分析图解
代码实现基数算法
常用排序算法总结和对比
查找算法
查找算法介绍
线性查找
基本介绍
算法分析图解
代码实现线性查找算法
二分查找
基本介绍
算法分析图解
代码实现线二分查找算法
插值查找
基本介绍
算法分析图解
代码实现插值查找算法
斐波那契(黄金分割)查找
基本介绍
算法分析图解
代码实现斐波那契(黄金分割)查找算法
哈希表
哈希表的基本原理
哈希表(散列)-Google上机题
实现哈希表[数组+链表]
- 哈希表创建
- 哈希表的添加
- 哈希表的查找