数据结构:第二章(一)

视频链接:https://www.bilibili.com/video/BV1HQ4y1d7th

视频范围P42 - P64

1.稀疏数组

压缩条件

  1. 原数组中存在大量的无效数据,占据了大量的存储空间,真正有用的数据很少
  2. 压缩存储可以节省存储空间,避免资源的不必要的浪费,在数据序列化到磁盘时,压缩存储可以提高IO效率

稀疏数组处理方法

  1. 记录数组一共有几行几列,有多少个不同的值
  2. 把具有不同值元素的行列及值记录在一个小规模的数组中,从而缩小程序的规模

普通压缩方式
在这里插入图片描述
链式存储压缩方式

在这里插入图片描述

1.1 压缩数据至稀疏数组

package arrayPractice;

public class SparseArray {
    public static void main(String[] args) {
        //模拟出来棋盘数据,使用二维数组
        int[][] array = new int[11][11];
        array[1][2] = 1;
        array[2][4] = 2;

        //打印棋盘查看效果
        for (int[] row : array){
            for (int val : row){
                System.out.printf("%d\t",val);
            }
            System.out.println();
        }

        //需要把如上的二维数组中有效数据压缩至稀疏数组中去
        int sum = 0;
        for (int i = 0; i < 11; i++) {
            for (int j = 0; j < 11; j++) {
                if (array[i][j] != 0){
                    sum++;
                }
            }
        }
        //System.out.println("有效数据个数:" + sum);

        //定义稀疏数组
        int[][] parseArray = new int[sum + 1][3];
        parseArray[0][0] = 11;
        parseArray[0][1] = 11;
        parseArray[0][2] = sum;

        //把有效数据存放至稀疏数组中去
        int count = 0;
        for (int i = 0; i < 11; i++) {//行的索引
            for (int j = 0; j < 11; j++) {//列的索引
                //判断是否是有效数据
                if (array[i][j] != 0){
                    count++;
                    parseArray[count][0] = i;
                    parseArray[count][1] = j;
                    parseArray[count][2] = array[i][j];
                }
            }
        }

        //打印稀疏数组
        for (int[] row : parseArray){
            for (int val : row){
                System.out.printf("%d\t",val);
            }
            System.out.println();
        }

        //把稀疏数组转原始二维数组【面试题】
        int[][] oldArray = new int[parseArray[0][0]][parseArray[0][1]];
        for (int i = 1 ; i <= count; i++) {
            oldArray[parseArray[i][0]][parseArray[i][1]] = parseArray[i][2];
        }

        //查看原始二维数组棋盘
        for (int[] row : oldArray){
            for (int val : row){
                System.out.printf("%d\t",val);
            }
            System.out.println();
        }
    }
}

运行结果

在这里插入图片描述

1.2 压缩数据至稀疏链表

2. 队列

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头
在这里插入图片描述

  1. 队列是一个有序列表,可以用数组和链表来实现
  2. 队列先进先出,即是先入队列的数据最先被取出,后存入的数据后取出

2.1 数组实现队列(非循环队列)

队列类

package queuePractice;

public class ArrayQueue {
    private int[] array;
    private int maxSize;
    private int frontPoint;
    private int rearPoint;

    public ArrayQueue(int maxSize) {
        this.maxSize = maxSize;
        array = new int[maxSize];

        frontPoint = rearPoint = -1;
    }

    //判断当前队列是否是满
    public boolean isFull(){
        return rearPoint == maxSize - 1;
    }

    //判断是否是空队列
    public boolean isEmpty(){
        return frontPoint == rearPoint;
    }

    //添加元素进入队列
    public void add(int n){
        //判断是否已满
        if (isFull()){
            System.out.println("队列已满");
            return;
        }
        rearPoint++;
        array[rearPoint] = n;
    }

    //获取队列元素并且删除该元素,出队列
    public int get(){
        //判断是否空
        if (isEmpty()){
            throw new RuntimeException("空队列");
        }
        frontPoint++;
        return array[frontPoint];
    }

    //查看队列中的元素
    public void findQueue(){
        //判断是否空
        if (isEmpty()){
            throw new RuntimeException("空队列");
        }

        //i设置为frontPoint + 1为了去除出队列的数据
        for (int i = frontPoint + 1; i < array.length; i++) {
            System.out.printf("array[%d] = %d\n",i,array[i]);
        }
    }

    //查看队头的元素,不是出队列
    public int frontQueue(){
        //判断是否空
        if (isEmpty()){
            throw new RuntimeException("空队列");
        }
        return array[frontPoint + 1];
    }
}

测试类

package queuePractice;

public class TestApp {
    public static void main(String[] args) {
        //初始化队列
        ArrayQueue arrayQueue = new ArrayQueue(5);

        //向队列中添加元素
        arrayQueue.add(1);
        arrayQueue.add(2);
        arrayQueue.add(3);
        arrayQueue.add(4);
        arrayQueue.add(5);

        //获取队列头元素并且删除该元素,出队列
        int i = arrayQueue.get();
        System.out.println(i);

        //打印队列中的元素
        arrayQueue.findQueue();
    }
}

运行结果

在这里插入图片描述

2.2 链表实现队列

3.递归

概念
递归就是方法自己去调用自己,每次调用时传入的参数是不同的,递归有助于解决程序中复杂的问题,同时可以让代码更为简洁

递归解决的问题

  1. 可以解决各种数学问题,汉若塔,迷宫问题,8皇后问题等等
  2. 各种算法的递归,如快排,归并排序,二分查找,分治算法等

递归的规则

  1. 执行一个方法时,就创建一个新的受保护的独立栈空间
  2. 方法的局部变量是独立的,不会相互影响。
  3. 如果方法中使用的是引用型类型变量,比如数组,则会共享引用型的数据
  4. 递归必须向退出递归的条件接近,否则就是无限递归,会出现StackOverflowError
  5. 一个方法执行完毕后,或者遇到return,或就返回数据,遵守谁调用就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕。

3.1 迷宫格实例

在这里插入图片描述

package maze;

public class MazeApp {
    public static void main(String[] args) {
        int[][] map = new int[8][7];

        //设置第一行和最后一行为墙,设置为1
        for (int i = 0; i < 7; i++) {
            map[0][i] = 1;
            map[7][i] = 1;
        }

        //设置第一列和最后一列为墙,设置为1
        for (int i = 0; i < 8; i++) {
            map[i][0] = 1;
            map[i][6] = 1;
        }

        //设置障碍墙
        map[3][1] = 1;
        map[3][2] = 1;

        System.out.println("==========定义迷宫==========");

        //打印输出
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 7; j++) {
                System.out.print(map[i][j] + " ");
            }
            System.out.println();
        }

        isRun(map,1,1);

        System.out.println("==========小球走过的路线==========");

        //打印输出
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 7; j++) {
                System.out.print(map[i][j] + " ");
            }
            System.out.println();
        }

    }

    //小球起始位置[1,1]  map是地图对象  小球最终位置[6,5]
    //元素为0时表示该点没有走过,元素为1表示是墙
    //元素为2表示通过可以走,元素3表示已经走过了,但是走不通
    public static boolean isRun(int[][] map,int i, int j){
        if (map[6][5] == 2){
            return true;
        }else {
            if (map[i][j] == 0){//没有走过该点
                map[i][j] = 2;

                //下,右,上,左
                if (isRun(map,i + 1,j)){
                    return true;
                }else if(isRun(map,i,j + 1)){
                    return true;
                }else if(isRun(map,i - 1,j)){
                    return true;
                }else if(isRun(map,i,j - 1)){
                    return true;
                }else{
                    map[i][j] = 3;
                    return false;
                }
            }else {
                return false;
            }
        }
    }
}

运行结果:

在这里插入图片描述

4.排序算法

4.1 简介

排序也称之为排序算法(Sort Algorithm),是将一组数据以指定的顺序进行排列的过程。

排序分类概念
内部排序将所有的数据都加载到内部存储器上进行排序
外部排序数据量比较大,无法全部加载到内存中,需要借助外部存储(文档)进行排序

算法分类图
在这里插入图片描述

4.2 算法时间效率

4.2.1 度量一个程序执行时间两种方法

度量方法概念
事后统计方法这种方法可行,但是存在于两个问题:一是要想对设计的算法运行性能进行评测就需要实际去运行该程序,而且所得时间统计量依赖于计算机硬件、软件等因素,这种方式要在同一台计算机的相同状态下运行,才能比较出哪一个算法速度更快,更好。
事前统计方法通过分析某一个算法的时间复杂度来判断哪个算法更优,更好。

4.2.2 时间频度

时间频度:一个算法花费的时间与算法中语句的执行次数成正比,哪一个算法中语句执行次数多,那么他所花费的时间就会多。一个算法中语句执行次数称之为语句频度时间频度。记为T(n)

忽略常数项,忽略低次项,忽略系数

4.2.3 时间复杂度

  1. 在计算机科学中,时间复杂性,又称时间复杂度,算法的时间复杂度是一个函数,它定性描述该算法的运行时间。
  2. 这是一个代表算法输入值的字符串的长度的函数。
  3. 时间复杂度常用大О符号表述,不包括这个函数的低阶项和首项系数。
  4. 使用这种方式时,时间复杂度可被称为是渐近的,渐近时间复杂度又称之为时间复杂度。

在这里插入图片描述

4.2.4 平均和最坏时间复杂度

  1. 平均时间复杂度:所有可能的输入实例均以等概率的出现情况下得到算法的运行时间
  2. 最坏时间复杂度:一般讨论的时间复杂度均是最坏情况下的时间复杂度,这样做的原因是最坏情况下的时间复杂度是算法在任何输入实例上运行的界限,这就保证了算法的运行时间不会比最坏情况更长。
  3. 平均时间复杂度和最坏时间复杂度是否一样,这就需要根掘算法不同而不同了。

在这里插入图片描述

4.3 基数排序

  1. 基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort
  2. 顾名思义,它是透过键值的部分资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O(nlog ( r )m)
  3. 基数排序是1887年赫尔曼、何乐礼发明的
  4. 思想:将整数按位数切割成不同的数字,然后按每个位数分别比较。

具体思想

  1. 将所有的待比较数值统一设置为同样的数位长度,位数比较短的数前面补零
  2. 然后从最低位开始依次进行一次排序
  3. 这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。

在这里插入图片描述

package sort;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

public class BasicSort {
    public static void main(String[] args) {

        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-mm-dd HH:MM:ss:SSS");
        System.out.println("开始时间:" + simpleDateFormat.format(new Date()));

        int[] arrays = new int[]{53,542,3,63,14,214,154,748,616};
        sort(arrays);
        
        System.out.println("结束时间:" + simpleDateFormat.format(new Date()));
        System.out.println(Arrays.toString(arrays));//[3, 14, 53, 63, 154, 214, 542, 616, 748]
    }

    public static void sort(int[] arrays){

        //获取最大位数
        int max = 0;
        for (int i = 0; i < arrays.length; i++) {
            if (arrays[i] > max){
                 max = arrays[i];//获取最大值
            }
        }

        //获取字符串长度,所有把int类型转字符串类型
        int maxLength = (max + "").length();

        //定义二维数组,大小10,表示10个桶,每一个桶则是一个数组[ [],[],[],[],[]...]
        int[][] bucket = new int[10][arrays.length];

        //辅助数组
        int[] bucketElementCount = new int[10];

        //第一轮针对个位数进行比较
        //循环获取无序数列 按照个位将数据放入桶中
        for (int i = 0; i < arrays.length; i++) {
            int locationElement = arrays[i] % 10;

            //放入桶中
            bucket[locationElement][bucketElementCount[locationElement]] = arrays[i];
            bucketElementCount[locationElement]++;
        }

        //遍历每一个桶,将元素存放到原数组当中去
        int index = 0;

        for (int i = 0; i < bucketElementCount.length; i++) {
            if (bucketElementCount[i] != 0){
                for (int j = 0; j < bucketElementCount[i]; j++) {
                    arrays[index++] = bucket[i][j];
                }
            }
            bucketElementCount[i] = 0;
        }

        System.out.println(Arrays.toString(arrays));//[542, 53, 3, 63, 14, 214, 154, 616, 748]

        //第二轮针对十位数进行比较
        for (int i = 0; i < arrays.length; i++) {
            int locationElement = arrays[i] / 10 % 10;

            //放入桶中
            bucket[locationElement][bucketElementCount[locationElement]] = arrays[i];
            bucketElementCount[locationElement]++;
        }

        //取出来按照桶的顺序放回原数组中
        int indx = 0;
        for (int i = 0; i < bucketElementCount.length; i++) {
            if (bucketElementCount[i] != 0){
                for (int j = 0; j < bucketElementCount[i]; j++) {
                    arrays[indx++] = bucket[i][j];
                }
            }
            bucketElementCount[i] = 0;
        }

        System.out.println(Arrays.toString(arrays));//[3, 14, 214, 616, 542, 748, 53, 154, 63]

        //第三轮针对百位数进行比较
        for (int i = 0; i < arrays.length; i++) {
            int locationElement = arrays[i] / 100 % 10;

            //放入桶中
            bucket[locationElement][bucketElementCount[locationElement]] = arrays[i];
            bucketElementCount[locationElement]++;
        }

        //取出来按照桶的顺序放回原数组中
        int inx = 0;
        for (int i = 0; i < bucketElementCount.length; i++) {
            if (bucketElementCount[i] != 0){
                for (int j = 0; j < bucketElementCount[i]; j++) {
                    arrays[inx++] = bucket[i][j];
                }
            }
            bucketElementCount[i] = 0;
        }
        System.out.println(Arrays.toString(arrays));//[3, 14, 53, 63, 154, 214, 542, 616, 748]
    }
}

运行结果:

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值