作者:皮皮猫吖!
本篇文章:
数据结构与算法是程序猿的必修课,学好数据结构与算法,对于敲代码会有很大的提升。
本篇文章主要是关于数据结构与算法的一些基本知识:线性结构、非线性结构、稀疏矩阵、队列。
正文如下:
1、数据结构包括什么?
数据结构包括:线性结构、非线性结构
2、线性结构:
1)线性结构作为最常用的数据结构,其特点是数据元素之间存在一对一的线性关系
2)线性结构有两种不同的存储结构,即顺序存储结构和链式存储结构。顺序存储【存储数据的地址相连】的线性表称为顺序表,顺序表中的存储元素【存储元素的地址】是连续的
3)链式存储的线性表称为链表,链表中的存储元素【存储元素的地址】不一定是连续的,元素节点中存放数据元素以及相邻元素的地址信息
4)线性结构常见的有:数组、队列、链表和栈,后面我们会详细讲解.
3、非线性结构
1)非线性结构包括:
二维数组,多维数组,广义表,树结构,图结构
4、稀疏数组
1)实际需求【五子棋的存盘和续盘操作】:
2)常规数组:
当一个数组中大部分元素为0,或者为同一个值的数组时,可以使用稀疏数组来保存该数组。
3)稀疏数组:
① 记录数组一共有几行几列,有多少个不同的值
② 把具有不同值的元素的行列及值记录在一个小规模的数组中,从而缩小程序的规模
4)例子:
① 二维数组==>稀疏数组
- 遍历原始二维数组,得到有效数据的个数sum
- 根据sum的值,创建稀疏数组sparseArr int[sum+1] [3]
- 将二维数组中的数据存储到稀疏数组中
② 稀疏数组==>二位数组
- 先读取稀疏数组的第一行,根据第一行的数据,创建原始的二维数组int chessArr2[int[0] [0]] [int[0] [1]]
- 在读取稀疏数组的后几行的数据的时候,直接赋值到对应的二维数组位置即可
③ 二维数组==>稀疏数组代码:
- 稀疏数组不写入文件中
@Test
public void sparseArrTest(){
int[][] chessArr = new int[][]{
{0,0,0,22,0,0,15},
{0,11,0,0,0,17,0},
{0,0,0,-6,0,0,0},
{0,0,0,0,0,39,0},
{91,0,0,0,0,0,0},
{0,0,28,0,0,0,0}
};
int count = 0;
//判断出二维数组中非0的个数
for(int i = 0; i < 6; i++){
for(int j = 0; j < chessArr[i].length; j++){
// System.out.print(chessArr[i][j]+" ");
if(chessArr[i][j]!=0){
count++;
}
}
}
//创建稀疏数组,并将二维数组中的值赋值给稀疏数组
int index = 1;
int[][] sparseArr = new int[count+1][3];
for(int i = 0; i < 6; i++){
for(int j = 0; j < chessArr[i].length; j++){
// System.out.print(chessArr[i][j]+" ");
if(chessArr[i][j]!=0){
sparseArr[index][0] = i;
sparseArr[index][1] = j;
sparseArr[index++][2] = chessArr[i][j];
}
}
}
//遍历稀疏数组
sparseArr[0][0] = 6;
sparseArr[0][1] = chessArr[0].length;
sparseArr[0][2] = count;
for (int i = 0; i <= count; i++){
for(int j = 0; j < 3; j++){
System.out.print(sparseArr[i][j]+" ");
}
System.out.println();
}
}
- 将得到的稀疏数组写入到data.txt文件中
@Test
public void sparseArrTest() throws IOException {
int[][] chessArr = new int[][]{
{0,0,0,22,0,0,15},
{0,11,0,0,0,17,0},
{0,0,0,-6,0,0,0},
{0,0,0,0,0,39,0},
{91,0,0,0,0,0,0},
{0,0,28,0,0,0,0}
};
int count = 0;
for(int i = 0; i < 6; i++){
for(int j = 0; j < chessArr[i].length; j++){
// System.out.print(chessArr[i][j]+" ");
if(chessArr[i][j]!=0){
count++;
}
}
}
int index = 1;
int[][] sparseArr = new int[count+1][3];
for(int i = 0; i < 6; i++){
for(int j = 0; j < chessArr[i].length; j++){
// System.out.print(chessArr[i][j]+" ");
if(chessArr[i][j]!=0){
sparseArr[index][0] = i;
sparseArr[index][1] = j;
sparseArr[index++][2] = chessArr[i][j];
}
}
}
sparseArr[0][0] = 6;
sparseArr[0][1] = chessArr[0].length;
sparseArr[0][2] = count;
for (int i = 0; i <= count; i++){
for(int j = 0; j < 3; j++){
System.out.print(sparseArr[i][j]+" ");
}
System.out.println();
}
File file = new File("data.txt");
FileWriter fw = new FileWriter(file);
for (int i = 0; i <= count; i++){
for(int j = 0; j < 3; j++){
fw.write(sparseArr[i][j]+"\t");
}
fw.write("\n");
}
fw.close();
}
④ 稀疏数组==>二维数组代码
- 稀疏数组自定义:
@Test
public void chessArrTest(){
int[][] sparseArr = new int[][]{
{6,7,8},
{0,3,22},
{0,6,15},
{1,1,11},
{1,5,17},
{2,3,-6},
{3,5,39},
{4,0,91},
{5,2,28}
};
//创建二维数组
int[][] chessArr = new int[sparseArr[0][0]][sparseArr[0][1]];
//稀疏数组的值,赋值给二维数组
for (int i = 1; i <= sparseArr[0][2]; i++){
chessArr[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2];
}
//遍历二维数组
for (int i = 0; i < sparseArr[0][0]; i++){
for (int j = 0; j < sparseArr[0][1]; j++){
System.out.print(chessArr[i][j] + " ");
}
System.out.println();
}
}
- 从data.txt文件中读取稀疏数组
@Test
public void chessArrTest() throws IOException{
File file = new File("data.txt");
FileReader fr = new FileReader(file);
FileReader fr1 = new FileReader(file);
int[][] sparseArr = new int[][]{
{6,7,8},
{0,3,22},
{0,6,15},
{1,1,11},
{1,5,17},
{2,3,-6},
{3,5,39},
{4,0,91},
{5,2,28}
};
int data;
char dat;
int row_num = 0;
int col_num = 0;
while((data=fr.read())!=-1){
dat = (char)data;
if(row_num == 0 && dat == '\t'){
col_num++;
}else if(dat == '\n'){
row_num++;
}
}
fr.close();
int[][] sparseArray = new int[row_num][col_num];
int row = 0;
int col = 0;
while((data=fr1.read())!=-1){
dat = (char)data;
if(dat>='0' && dat <= '9') {
sparseArray[row][col] = (sparseArray[row][col]*10+(dat - '0'));
}else if(dat == '\t'){
col++;
}else if(dat == '\n'){
row++;
col = 0;
}
}
fr1.close();
int[][] chessArr = new int[sparseArray[0][0]][sparseArray[0][1]];
for (int i = 1; i <= sparseArray[0][2]; i++){
chessArr[sparseArray[i][0]][sparseArray[i][1]] = sparseArray[i][2];
}
for (int i = 0; i < sparseArray[0][0]; i++){
for (int j = 0; j < sparseArray[0][1]; j++){
System.out.print(chessArr[i][j] + " ");
}
System.out.println();
}
}
5、队列
1)队列是什么?
① 队列是一个有序列表,可以用数组或是链表来实现。
② 队列遵循先入先出的原则。即:先存入队列的数据,要先取出。后存入的要后取出
③ 示意图:(使用数组模拟队列示意图)
2)数组模拟队列
① 队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图, 其中 maxSize 是该队列的最大容量。
② 因为队列的输出、输入是分别从前后端来处理,因此需要两个变量front及rear分别记录队列前后端的下标。
front:指向当前队列的第一个元素的前一个元素,会随着数据输出而改变
rear:指向当前队列的最后一个元素,随着数据输入而改变
3)数组模拟队列【单向队列思路】
① 入队思路:
- 首先判断当前队列是否已满?【rear == maxSize-1】
- 队列已满,无法添加数据;
- 队列未满:队列将尾指针往后移:rear+1
② 出队思路:
- 首先判断当前队列是否为空?【rear == front】
- 队列为空,队列中没有数据,无法出队
- 队列不为空,可以出队:队列头指针向后移:front+1
4)模拟队列【单向队列代码】
package com.data.structure.queue;
import java.io.IOException;
import java.util.Scanner;
/**
* @author imppm
* @create 2021-02-10-11:29
*/
public class ArrayQueueDemo {
public static void main(String[] args) {
menu();
}
private static void menu(){
Scanner scanner = new Scanner(System.in);
ArrayQueue arrayQueue = null;
while(true){
System.out.println("1.创建队列");
System.out.println("2.入队");
System.out.println("3.出队");
System.out.println("4.显示队列当前数据");
System.out.println("5.退出");
System.out.print("请输入你的选择(1-5):");
int choice = scanner.nextInt();
switch (choice) {
case 1:
System.out.print("请输入需要创建队列的长度:");
int length = scanner.nextInt();
arrayQueue = new ArrayQueue(length);
break;
case 2:
System.out.print("请输入入队的数据:");
int data = scanner.nextInt();
if(arrayQueue!=null) {
if(arrayQueue.push(data)){
System.out.println("数据:"+data+"入队成功。");
}else{
System.out.println("数据"+data+"入队失败。");
}
}
break;
case 3:
if(arrayQueue!=null) {
try {
int pop = arrayQueue.pop();
System.out.println("数据" + pop + "出队成功!");
}catch (Exception e){
System.out.println(e.getMessage());
}
}else{
System.out.println("出队失败");
}
break;
case 4:
System.out.println("队列数据如下:");
if(arrayQueue!=null){
arrayQueue.showQueue();
}
break;
case 5:
System.out.print("是否确认退出(Y/N):");
String result = scanner.next();
if("Y".equals(result)){
return;
}
}
}
}
}
//使用数组来模拟队列,编写一个ArrayQueue的队列
class ArrayQueue{
//队列头
private int front;
//队列尾
private int rear;
//该数据用于存放数据,模拟队列
private int[] queue;
//队列存储的最大数据量
private int maxSize;
//创建队列的构造器:初始化队列头、队列尾、队列最大存储量
public ArrayQueue(int maxSize){
this.maxSize = maxSize;
queue = new int[this.maxSize];
front = -1;//指向队列第一个数据的前一个位置
rear = -1;//指向队列尾部的数据【初始化无数据,指向第一个数据的前一个位置】
}
//判断队列是否已满
private boolean isFull(){
return rear == (maxSize - 1);
}
//判断队列是否为空
private boolean isEmpty(){
return rear == front;
}
//入队
public boolean push(int data){
if(isFull()){
return false;
}else{
queue[++rear] = data;
return true;
}
}
//出队
public int pop(){
if(isEmpty()){
throw new RuntimeException("队列没有数据,无法获取数字");
}else {
int data = queue[++front];
return data;
}
}
//显示队列所有数据
public void showQueue(){
//忘记加上,队列为空
if(isEmpty()){
System.out.println("队列为空,没有数据");
return;
}
for (int i = front+1; i <= rear; i++){
System.out.print(queue[i]+" ");
}
System.out.println();
}
//获取队列头元素
public int headQueue(){
if(isEmpty()){
System.out.println("队列为空,没有数据");
throw new RuntimeException("队列为空,没有数据");
}
return queue[front+1];
}
}
5)单向队列存在的问题及优化:
① 问题:
- 目前队列使用一次就不能用了,没有达到复用的效果
② 优化:
- 使用一定的算法,将单向队列改进成一个环形队列,环形队列可以实现复用操作
6)数组模拟队列【环形队列思路】
① 环形队列设计规范【队列空、队列满、rear、front】:
- 为了区分队列为空和队列已满,把最大容量-1为当前队列已满,把rear == front为队列为空
- rear的含义更改为:rear指向队列的最后一个元素的下一个元素,rear的初始值为0
- front的含义更改为:front指向队列的第一个元素,front的初始值为0
② 入队思路
- 首先需要判断当前队列是否已满?【(rear+1)%maxSize==front】
- 队列已满,无法添加数据
- 队列未满,可以添加数据,(rear+1)%maxSize
③ 出队思路
- 首先需要判断当前队列是否为空?【rear == front】
- 队列为空,无法读取数据
- 队列不为空,可以读取数据,(front+1)%maxSize
④ 环形队列的数据总量
- (rear + maxSize - front) % maxSize
7)数组模拟队列【环形队列代码】
package com.data.structure.queue;
import java.util.Scanner;
/**
* @author imppm
* @create 2021-02-10-13:58
*/
public class CircleArrayQueueDemo {
public static void main(String[] args) {
menu();
}
private static void menu(){
Scanner scanner = new Scanner(System.in);
CircleArrayQueue circleArrayQueue = null;
while(true){
System.out.println("1.创建一个队列");
System.out.println("2.添加数据到队列");
System.out.println("3.读取队列中数据");
System.out.println("4.显示队列所有数据");
System.out.println("5.显示队列数据个数");
System.out.println("6.退出");
System.out.print("请输入你的选择(1-6):");
int choice = scanner.nextInt();
switch (choice) {
case 1:
System.out.print("请输入需要创建队列的长度:");
int length = scanner.nextInt();
circleArrayQueue = new CircleArrayQueue(length);
break;
case 2:
System.out.print("请输入入队的数据:");
int data = scanner.nextInt();
if(circleArrayQueue!=null) {
circleArrayQueue.push(data);
}
break;
case 3:
if(circleArrayQueue!=null) {
try {
circleArrayQueue.pop();
}catch (Exception e){
System.out.println(e.getMessage());
System.out.println("------------------------");
}
}else{
System.out.println("出队失败");
System.out.println("------------------------");
}
break;
case 4:
System.out.println("队列数据如下:");
if(circleArrayQueue!=null){
circleArrayQueue.showQueue();
}
break;
case 5:
if(circleArrayQueue!=null) {
int len = circleArrayQueue.queueDataNum();
System.out.println("队列的数据长度为"+len);
System.out.println("------------------------");
}
break;
case 6:
System.out.print("是否确认退出(Y/N):");
char result = scanner.next().toUpperCase().charAt(0);
if('Y' == result){
return;
}
}
}
}
}
class CircleArrayQueue{
//队尾
private int rear;
//队头
private int front;
//队列存储最大数据量
private int maxSize;
//数组用来模拟队列
private int[] queue;
//创建队列
public CircleArrayQueue(int maxSize){
this.maxSize = maxSize;
queue = new int[this.maxSize];
rear = 0;
front = 0;
}
//队列是否为空
private boolean isEmpty(){
return front == rear;
}
//队列已满
private boolean isFull(){
return (rear + 1)%maxSize == front;
}
//入队操作
public void push(int data){
if(isFull()){
System.out.println("队列已满,无法入队。");
System.out.println("------------------------");
}else{
//1. rear指向队列最后一个元素的下一个元素,先赋值
//2. rear+1
//3. rear取模
queue[rear] = data;
rear++;
rear %= maxSize;
System.out.println("数据"+data+"入队成功。");
System.out.println("------------------------");
}
}
//出队操作
public int pop(){
if(isEmpty()){
System.out.println("队列为空,出队失败");
System.out.println("------------------------");
throw new RuntimeException("队列为空,出队失败。");
}else{
//1.front指向队列第一个数据。去除队列第一个数据
//2.front+1
//3.front进行取模操作
int data = queue[front];
front++;
front %= maxSize;
System.out.println("数据"+data+"出队成功。");
System.out.println("------------------------");
return data;
}
}
//获取队列长度
public int queueDataNum(){
return (rear+maxSize-front)%maxSize;
}
//显示队列所有数据
public void showQueue(){
int f = front;
int r = rear;
int data;
while (f != r){
data = queue[f];
f++;
f = f % maxSize;
System.out.print(data+" ");
}
System.out.println();
System.out.println("------------------------");
}
}
希望本篇文章对大家有所帮助