【Java数据结构与算法】基础篇(1):稀疏数组与队列

10 篇文章 0 订阅
9 篇文章 0 订阅

大家好,我是皮皮猫吖!

每文一言:少一些功利主义的追求,多一些不为什么的坚持!

本篇文章:

数据结构与算法是程序猿的必修课,学好数据结构与算法,对于敲代码会有很大的提升。学好java数据结构,冲冲冲!

本篇文章主要是关于数据结构与算法的一些基本知识:线性结构、非线性结构、稀疏矩阵、队列。

正文如下:

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 是该队列的最大容量。

② 因为队列的输出、输入是分别从前后端来处理,因此需要两个变量frontrear分别记录队列前后端的下标。

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("------------------------");
    }

}


希望本篇文章对大家有所帮助,后续会继续分享java数据结构与算法相关学习知识…

如果文章内容有错误的地方,请在留言处留下你的见解,方便大家共同学习。谢谢!

如有侵权或其他任何问题请联系:QQ1370922071,本文主要用于学习交流,转载请声明!

作者:皮皮猫吖


  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值