一、数据结构和算法概念
什么是数据结构?
- 数据结构(data structure)是带有结构特性的数据元素的集合,它研究的是数据的逻辑结构和数据的物理结构以及它们之间的相互关系,并对这种结构定义相适应的运算,设计出相应的算法,并确保经过这些运算以后所得到的新结构仍保持原来的结构类型。简而言之,数据结构是相互之间存在一种或多种特定关系的数据元素的集合,即带“结构”的数据元素的集合。
- “结构”就是指数据元素之间存在的关系,分为逻辑结构、存储结构和物理结构。
什么是算法?
-
算法(Algorithm)是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制
-
一个算法应该具有以下五个重要的特征:
-
有穷性(Finiteness)
- 算法的有穷性是指算法必须能在执行有限个步骤之后终止;
-
确切性(Definiteness)
- 算法的每一步骤必须有确切的定义;
-
输入项(Input)
- 一个算法有0个或多个输入,以刻画运算对象的初始情况,所谓0个输入是指算法本身定出了初始条件
-
输出项(Output)
- 一个算法有一个或多个输出,以反映对输入数据加工后的结果。没有输出的算法是毫无意义的;
-
可行性(Effectiveness)
- 算法中执行的任何计算步骤都是可以被分解为基本的可执行的操作步骤,即每个计算步骤都可以在有限时间内完成(也称之为有效性)。
-
2、数据结构和算法关系
- 数据结构是算法的基础
- 程序=数据结构+算法
3、线性结构和非线性结构
- 数据结构有很多种,一般来说,按照数据的逻辑结构对其进行简单的分类,包括线性结构和非线性结构两类
* 线性结构:线性结构就是表中各个结点具有线性关系
-
线性结构应该包括如下几点:
- 线性结构是非空集。
- 线性结构有且仅有一个开始结点和一个终端结点。
- 线性结构所有结点都最多只有一个直接前驱结点和一个直接后继结点。
-
线性结构常见的有:数组、队列、链表和栈
-
线性结构的存储结构
- 顺序存储结构
- 顺序存储的线性表称为顺序表,顺序表中的存储元素是连续的
- 链式存储结构:
- 链式存储的线性表称为链表,链表中的存储元素不一定是连续的,元素节点中存放数据元素以及相邻元素的地址信息
- 顺序存储结构
-
非线性结构: 非线性结构就是表中各个结点之间具有多个对应关系
- 线性结构应该包括如下几点:
- 非线性结构是非空集.
- 非线性结构的一个结点可能有多个直接前驱结点和多个直接后继结点。
- 在实际应用中,二维数组、多维数组、广义表、树结构和图结构等数据结构都属于非线性结构
- 线性结构应该包括如下几点:
二、数组
1、数组概述:
数组的定义:
- 数组是相同数据类型的有序集合
- 数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成
- 其中,每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问它们
数组的基本特点:
- 其长度是确定的,数组一旦被创建,它的大小就是不可改变的
- 其元素必须是相同类型,不允许出现混合类型
- 数组中的元素可以是任何数据类型,包括基本类型和引用类型
- 数组变量属于引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量。数组本身就是对象,Java中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,数组对象本身是在堆中的
1、稀疏数组
引入问题:五子棋盘:
如上图所示,将棋盘抽象化为二维数组并进行数据压缩,利用稀疏数组进行值转换。写出上述代码:
-
稀疏数组的特点:
- 稀疏数组第一行数据表示:原始数组共几行,共几列,共几个非零值。
- 稀疏数组除第一行外的每行(非首行)分别记录每一个非零值所在行列坐标和数据值大小
-
代码实现:
- Java实现:
package 算法.数组.稀疏数组;
//稀疏数组的创建和还原
public class SparseArray {
public static void main(String[] args) {
//一、创建一个五子棋盘,将棋盘抽象化,转换成二维数组
int array[][]=new int[10][10];
array[2][2]=1;
array[4][5]=8;
array[9][9]=5;
//标记位
int count=0;
//打印原二维数组
System.out.println("原二维数组:");
for (int[] ints : array) {
for (int ints1 : ints) {
if (ints1!=0){
count++;
}
System.out.print(" "+ints1);
}
System.out.println();
}
System.out.println("非零元素:"+count);
System.out.println("==============================");
//二、转换成稀疏数组,创建稀疏数组,并将棋盘棋子即二维数组非零元素填充
/*
* row col value
* 0 [二维数组总行数] [二维数组总列数] [非零元素总个数]
* 1 [非零元素行] [非零元素行] [非零元素值]
* 思考:那么创建的稀疏数组行数和列数怎么获得,通过获取非零元素的总个数+1[第一行是固定获取二维数组的行列值]可以获得行数,列数固定为3
*
*/
int[][] sparse= new int[count+1][3];
//三、固定第一行,对第一行进行赋值
sparse[0][0]=10;
sparse[0][1]=10;
sparse[0][2]=count;
//四、将非零元素填充
int count1=0;
for (int i = 0; i < array.length; i++) {
for(int j=0;j< array[i].length;j++){
if (array[i][j]!=0){
count1++;
sparse[count1][0]=i;
sparse[count1][1]=j;
sparse[count1][2]= array[i][j];
}
}
}
//五、同理,打印稀疏数组
System.out.println("稀疏数组:");
System.out.println("row col val");
for (int i = 0; i < sparse.length; i++) {
for (int j = 0; j <sparse[i].length ; j++) {
System.out.print(sparse[i][j]+" \t");
}
System.out.println("");
}
System.out.println("==========================");
//六、现已知稀疏数组,创建虚拟棋盘二维数组
int[][] array2 = new int[sparse[0][0]][sparse[0][1]];
//七、将稀疏数组中的值赋值到二维数组中,进行行列填充
for (int i = 1; i < sparse.length; i++) {
array2[sparse[i][0]][sparse[i][1]]=sparse[i][2];
}
//八、打印二维数组
for (int[] ints : array2) {
for (int ints1 : ints) {
System.out.print(" "+ints1);
}
System.out.println();
}
}
}
-
实现结果:
-
C语言实现:
- 代码展示:
//讲解稀疏数组的创建和还原
//导入标准输入输出库函数
#include <stdio.h>
#include <string.h>
//主函数
int main(void ){
//创建10*10的二维数组
int arrary[10][10]={0};
//一、对二维数组的某些部分进行赋值操作
arrary[2][2]=1;
arrary[4][5]=8;
arrary[7][9]=4;
arrary[9][2]=7;
//行
int row= sizeof(arrary)/sizeof (arrary[0]);
//列
int col= sizeof (arrary[0])/sizeof (arrary[0][0]);
//统计二维数组的非零元素,创建标记位
int count=0;
//二、打印原二维数组
printf("原二维数组:\n");
for (int i = 0; i < row; ++i) {
for (int j = 0; j < col; ++j) {
if (arrary[i][j]!=0){
count++;
}
printf("%d ",arrary[i][j]);
}
printf("\n");
}
printf("======================\n");
//转换成稀疏数组,创建稀疏数组,并将棋盘棋子即二维数组非零元素填充
/*
* row col value
* 0 [二维数组总行数] [二维数组总列数] [非零元素总个数]
* 1 [非零元素行] [非零元素行] [非零元素值]
* 思考:那么创建的稀疏数组行数和列数怎么获得,通过获取非零元素的总个数+1[第一行是固定获取二维数组的行列值]可以获得行数,列数固定为3
*
*/
int sparse[(count + 1)][3];
//三、固定第一行,对第一行进行赋值
sparse[0][0]=10;
sparse[0][1]=10;
sparse[0][2]=count;
//四、将非零元素填充
int count1=0;
for (int i = 0; i < row; i++) {
for(int j=0;j< col;j++){
if (arrary[i][j]!=0){
count1++;
sparse[count1][0]=i;
sparse[count1][1]=j;
sparse[count1][2]= arrary[i][j];
}
}
}
//五、同理、打印稀疏数组
printf("二维数组转换为稀疏数组:\n");
printf("row col val\n");
for (int i = 0; i < sizeof(sparse)/sizeof(sparse[0]); ++i) {
for (int j = 0; j < sizeof(sparse[0])/sizeof(sparse[0][0]); ++j) {
printf("%d\t\t",sparse[i][j]);
}
printf("\n");
}
printf("======================\n");
//将稀疏数组转换成原始二维数组,进行还原
//六、稀疏疏组第一行是存储的原始二维数组的行、列以及非零元素统计,因此只需要创建一个二维数组,其数组的行列为稀疏数组的值
int transpose[sparse[0][0]][sparse[0][1]];
//在测试过程中发现,C语言与java不同,未知的二维数组会随机分配数据,那么考虑:当前数组如何清零??
//memset函数在程序开始时初始化数组。这条命令这在你已经修改了数组之后又想将它重置为全0,
memset(transpose, 0, sizeof(transpose));
//遍历稀疏数组,将稀疏数组的值赋值到二维数组大红,进行行列填充
for (int i = 1; i < sizeof(sparse)/sizeof(sparse[0]); ++i) {
transpose[sparse[i][0]][sparse[i][1]]=sparse[i][2];
}
//打印新的二维数组
int flag=1;
printf("稀疏数组转换为二维数组:\n");
for (int i = 0; i < sizeof(transpose)/sizeof (transpose[0]); ++i) {
for (int j = 0; j < sizeof(transpose[0])/sizeof(transpose[0][0]); ++j) {
//因为在C语言中会所及配数,造成该二维数组数据混乱,需要想办法重置非零位
printf("%d ",transpose[i][j]);
}
printf("\n");
}
return 0;
}
-
结果展示:
-
注意事项:
- C语言中未知的数组会随机分配数值,因此在操作该数组之前需要进行清零处理,使用memset库函数