一.数据结构的分型
数据结构包括线性结构和非线性结构
线性结构:
1.线性结构是最常见的数据结构,其特点是数据元素之间一对一的线性关系
2.线性结构有两种不同的存储结构(数组)和链式存储结构(链表),顺序存储的线性表称为顺序表,顺序表中存储的元素是连续的
3.链式存储的表称为链表,链表中的存储元素不一定是连续的,元素节点中存放数据元素以及相邻元素的地址信息
4.线性结构常见的有:数组,队列,链表和栈
非线性结构:
包括二维数组,多维数组,广义表,图结构,树结构
二.稀疏数组
当一个数组中大部分元素为0或者同一个值的时候,可以使用稀疏数组来保存该数据
稀疏数组的处理方式:
1.记录数组中一共有几行几列,有多少个不同的值
2.把具有不同值的元素的行列及其值记录在一个小规模的数组中,从而缩小数据的规模
二维数组转稀疏数组
1.遍历原始的二维数组,得到有效数据的个数
2.根据有效数据的个数就可以创建相应的稀疏数组
3.将二维数组的有效数据填入到稀疏数组中
public static int[][] convertSparseArray(int[][] chaseArr){ /* * * 1.遍历原始的二维数组,得到有效数据的个数sum * 2.根据sum来创建稀疏数组 * 3.将二维数组的有效数据存入到稀疏数组 * */ int sum = 0; for (int i = 0; i < chaseArr.length; i++) { for (int j = 0; j < chaseArr[i].length; j++) { if (chaseArr[i][j] != 0){ sum++; } } } //创建稀疏数组 int[][] sparseArray = new int[sum+1][3]; //二维数组转稀疏数组 // 给稀疏数组赋值 sparseArray[0][0] = chaseArr.length; // 原二维数组的行 sparseArray[0][1] = chaseArr[0].length; // 原二维数组的列 sparseArray[0][2] = sum; // 原二维数组有效数据的个数 int count = 0; for (int i = 0; i < chaseArr.length; i++) { for (int j = 0; j < chaseArr[i].length; j++) { if(chaseArr[i][j] != 0){ count++; sparseArray[count][0] = i; // 获取有效值所在的行 sparseArray[count][1] = j; // 获取有效值所在的列 sparseArray[count][2] = chaseArr[i][j]; // 获取原数组中的有效值 } } } return sparseArray; }
稀疏数组还原成二维数组
1.先读取稀疏数组的第一行,根据第一行创建原始的二维数组
2.根据有效值,根据行列,传进二维数组
public static int[][] convertOriArrar(int[][] sparseArr){ /* * 稀疏数组转二维数组 * 1.先读取稀疏数组的第一行,根据第一行创建原始的二维数组 * 2.在读取稀疏数组的后几行数据,并赋给原始的二维数组即可 * */ int row = sparseArr[0][0]; int col = sparseArr[0][1]; int[][] chaseArr = new int[row][col]; for (int i = 1; i < sparseArr.length; i++) { chaseArr[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2]; } return chaseArr; }
完整版:
public class SparseArray { /*从二维数组转变为稀疏数组,在由稀疏数组转变成二维数组*/ public static void main(String[] args) { // 棋盘,1表示黑色,2表示白色 int[][] chaseArr = new int[11][11]; chaseArr[1][2] = 1; chaseArr[2][3] = 2; showArr(convertSparseArray(chaseArr)); int[][] sparseArr = convertSparseArray(chaseArr); showArr(convertOriArrar(sparseArr)); } public static int[][] convertOriArrar(int[][] sparseArr){ /* * 稀疏数组转二维数组 * 1.先读取稀疏数组的第一行,根据第一行创建原始的二维数组 * 2.在读取稀疏数组的后几行数据,并赋给原始的二维数组即可 * */ int row = sparseArr[0][0]; int col = sparseArr[0][1]; int[][] chaseArr = new int[row][col]; for (int i = 1; i < sparseArr.length; i++) { chaseArr[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2]; } return chaseArr; } public static int[][] convertSparseArray(int[][] chaseArr){ /* * * 1.遍历原始的二维数组,得到有效数据的个数sum * 2.根据sum来创建稀疏数组 * 3.将二维数组的有效数据存入到稀疏数组 * */ int sum = 0; for (int i = 0; i < chaseArr.length; i++) { for (int j = 0; j < chaseArr[i].length; j++) { if (chaseArr[i][j] != 0){ sum++; } } } //创建稀疏数组 int[][] sparseArray = new int[sum+1][3]; //二维数组转稀疏数组 // 给稀疏数组赋值 sparseArray[0][0] = chaseArr.length; // 原二维数组的行 sparseArray[0][1] = chaseArr[0].length; // 原二维数组的列 sparseArray[0][2] = sum; // 原二维数组有效数据的个数 int count = 0; for (int i = 0; i < chaseArr.length; i++) { for (int j = 0; j < chaseArr[i].length; j++) { if(chaseArr[i][j] != 0){ count++; sparseArray[count][0] = i; // 获取有效值所在的行 sparseArray[count][1] = j; // 获取有效值所在的列 sparseArray[count][2] = chaseArr[i][j]; // 获取原数组中的有效值 } } } return sparseArray; } public static void showArr(int[][] arr){ for (int[] row : arr) { for (int i : row) { System.out.printf("%d ",i); } System.out.println(); } } }
三.队列
1.队列是一个有序的列表,可以用数组或链表来实现
2.遵循先进先出的原则
3.数组模拟队列思路
队列本身是一个有序的列表,若使用数组的结构来存储队列,则队列数组的声明如下图,其中maxSize是队列的最大容量
因为队列的输出,输入分别从前后端来处理,因此需要front和rear两个变量分别记录队列前后端的下标,front会随着输出而改变,rear会随着输入而改变
示意图:
class ArrayQueue{ //模拟数组队列的实现类 private int maxSize; private int front; // 指向头部的指针,指向第一个元素的前一个位置 private int rear; // 指向尾部的指针,指向队列的最后一个元素 private int[] arr; public ArrayQueue(int maxSize){ this.maxSize = maxSize; front = -1; rear = -1; arr = new int[maxSize]; } /*判断队列是否为空*/ public boolean isEmpty(){ return front == rear; } /*判断队列是否已满*/ public boolean isFull(){ return rear == maxSize-1; } /*添加元素到队列中*/ public void addElement(int e){ if(isFull()){ System.out.println("队列已满,不能添加元素"); return; } rear++; // 尾部指针后移 arr[rear] = e; //添加元素到队列中 } /*获取队列中的元素*/ public int getElement(){ if (isEmpty()){ throw new RuntimeException("队列为空,不能取数据"); } front++; // 头指针后移 return arr[front]; // 获取元素 } /*打印队列中的元素*/ public void show(){ if (isEmpty()){ System.out.println("队列为空,没有数据"); return; } for (int i = 0; i < arr.length; i++) { System.out.printf("arr[%d]=%d ",i,arr[i]); } } /*打印队列的头部元素*/ public int getHeadElement(){ if (isEmpty()){ throw new RuntimeException("队列为空,没有数据"); } return arr[front+1]; } }
public static void main(String[] args) { testArrayQueue();//测试代码 } public static void testArrayQueue(){ // 初始化队列的元素,3个 ArrayQueue queue = new ArrayQueue(3); boolean loop = true; char key = ' '; Scanner scanner = new Scanner(System.in); while (loop){ System.out.println("请输入队列的操作,s(show) e(exit) a(add) g(get) h(head)"); //获取用户的输入操作 key = scanner.next().charAt(0); switch (key){ case 's': queue.show(); break; case 'a': int value = scanner.nextInt(); queue.addElement(value); break; case 'g': int res = queue.getElement(); System.out.println("取出的数是:"+res); break; case 'h': int head = queue.getHeadElement(); System.out.println("队列的头元素是:"+head); break; case 'e': scanner.close(); loop = false; break; default: break; } } System.out.println("程序退出"); }
执行结果:
队列无法复用,只能用一次,不能再次使用
4.改进
使用环形队列(通过取模的方式来实现)
5.思路
front指向队列中的第一个元素,arr[front]就是队列的第一个元素,初始值是0
rear指向队列的最后一个元素,arr[rear],就是队列的最后一个元素,需要预留出约定的空间
队列满时的条件:(rear+1)%maxSize == front
队列为空的条件: front == rear
队列中的有效个数:(rear+maxSize-front)%maxSize
class CircleArray{ /* * 1.front指向队列的第一个元素,也就是说arr[front]就是队列的第一个元素,front的初始值是0 * 2.rear指向队列的最后一个元素的后一个位置,要预留出一个空间作为约定 * 3.当队列满的时候(rear+1)%maxSize == front * 4.队列中的有效的数据个数(rear+maxSize-front)%maxSize * * */ private int maxSize; private int front; private int rear; private int[] arr; public CircleArray(int maxSize){ this.maxSize = maxSize; arr = new int[maxSize]; } /*判断队列是否为空*/ public boolean isEmpty(){ return front == rear; } /*判断队列是否已满*/ public boolean isFull(){ return (rear+1) % maxSize == front; } /*添加元素*/ public void addElement(int e){ if (isFull()){ System.out.println("队列已满,不能在添加元素"); return; } arr[rear] = e;// 直接加入数据 rear = (rear +1)%maxSize; //取模来实现环形例rear=1,maxSize=3 } /*获取队列元素*/ public int getElement(){ if (isEmpty()){ throw new RuntimeException("队列为空,不能获取元素"); } int v = arr[front]; // 把front对应的值保存到临时变量中 front = (front+1)%maxSize; // front指针取模,后移 return v; // 将临时变量返回 } /*打印输出队列*/ public void show(){ if (isEmpty()){ System.out.println("队列为空,没有数据"); return; } // 从front开始遍历,遍历队列中的有效元素的个数 for (int i = front; i < front+size(); i++) { System.out.printf("arr[%d]=%d ",(i%maxSize),arr[i%maxSize]); } } /*查看队列的第一个元素*/ public int getHeadElement(){ if (isEmpty()){ throw new RuntimeException("队列为空,不能打印队列"); } return arr[front]; } public int size(){ //例如: rear=2,front=1,maxSize=3 带入 有效的元素个数为1 return (rear+maxSize-front)%maxSize; } }
public static void main(String[] args) { testCircleQueue(); } public static void testCircleQueue(){ CircleArray circle = new CircleArray(4); boolean loop =true; char key = ' '; Scanner scanner = new Scanner(System.in); while (loop){ System.out.println("请输入队列的操作,s(show) e(exit) a(add) g(get) h(head)"); key = scanner.next().charAt(0); switch (key){ case 's': circle.show(); break; case 'a': circle.addElement(scanner.nextInt()); break; case 'g': System.out.println("取出的队列元素是:"+circle.getElement()); break; case 'h': System.out.println("队列的头部元素是:"+circle.getHeadElement()); break; case 'e': scanner.close(); loop = false; break; default: break; } } System.out.println("程序退出"); }
运行结果:
可以实现队列的复用