java数据结构(云课堂)

目录

1、概述

    1.1、什么是数据结构

    1.2、数据的存储结构

    1.3、数据的逻辑结构

    1.4、什么是算法

    1.5、算法的特性

    1.6、算法的基本要求

2、线性结构

    2.1、栈

    2.2、队列

    2.2.1、单向队列

    2.2.2、双端队列

    2.2.3、优先级队列

    2.3、单链表

    2.4、循环链表

    2.5、双链表

    2.6、递归

3、常见的时间复杂度

    3.1、算术表达式

     3.1.1、前缀表达式

     3.1.2、中缀表达式

     3.1.3、后缀表达式

    3.2、中缀表达式转换为后缀表达式

    3.3、抽象数据类型ADT

4、排序算法

    4.1、交换排序

      4.1.1、冒泡排序

      4.1.2、快速排序

    4.2、插入排序

      4.2.1、直接插入排序

      4.2.2、希尔排序

    4.3、选择排序 

      4.3.1、简单选择排序

      4.3.2、堆排序

    4.4、归并排序 

    4.5、基数排序 

5、树

6、堆

7、图


1、概述

    1.1、什么是数据结构

    数据与数据之间的关系

    1.2、数据的存储结构

    顺序存储、链式存储     

    1.3、数据的逻辑结构

    集合、线性、树形、图形

数据的逻辑结构:表示数据之间的关系
数据的存储结构:数据的逻辑结构在计算机内存中的实现
数据的运算(算法):对数据的一系列操作就是算法

    1.4、什么是算法

解决问题的思路

    1.5、算法的特性

    输入、输出、有穷性、确定性、可行性

    1.6、算法的基本要求

    正确性、可读性、健壮性、时间复杂度、空间复杂度

2、线性结构

    数组、栈、队列、单链表、循环链表、双链表、递归、排序算法

数组、栈、队列问题:插入元素比较麻烦

链表问题:查找元素比较麻烦

    2.1、栈

        先进后出、数组实现

应用场景:1、对字符串返序输出

                 2、像xml中分隔符<>、[]、{}......这些分隔符个数是否匹配、是否嵌套交叉

ArrayDeque虽然是双端队列,但是如果用push方法、pop方法就是栈(先进后出),

push方法实际调用的是addFirst方法,pop方法实际调用的是removeFirst方法。

    2.2、队列

        先进先出、数组实现

    2.2.1、单向队列

只能在一端插入数据、另一端删除数据

ArrayDeque虽然是双端队列,但是如果用add方法、remove方法就是单向队列(先进先出),

add方法实际调用的是addLast方法,remove方法实际调用的是removeFirst方法。

    2.2.2、双端队列

每一端都可以进行插入数据和删除数据:ArrayDeque

    2.2.3、优先级队列

数据项按照关键字进行排序,关键字的数据项往往在队列的最前面

    2.3、单链表

        节点只有next,没有prev
        如果只有一个对象,next指向自己(this)

    2.4、循环链表

没有末尾,因为末尾的node的next指向第一个,形成了一个环形

    2.5、双链表

        节点有next、prev
        如果只有一个对象,next、prev都指向自己(this)

    2.6、递归

        斐波那契数列:后一项等于前两项之和。eg:1 1 2 3 5 8 13......
        汉罗塔问题

/**
 * 汉罗塔问题
 * @createTime 2020年04月08日 16:37.
 */
public class TestHanRota {
    public static void main(String[] args) {
    hanRota(2,'A','B','C');
    }
    /**
     * 无论有多少个盘子,都认为只有两个。
     * 上面的所有盘子和最下面一个盘子
     * @param n 盘子的总数
     * @param from 开始的柱子
     * @param in 中间柱子
     * @param to 目标柱子
     */
    public static void hanRota(int n,char from,char in,char to){
        if(n==1){
            System.out.println("第1个盘子从"+from+"移动"+to);
        }else {
            //移动上面所有的盘子到中间位置
            hanRota(n-1,from,to,in);
            //移动最下面的一个盘子
            System.out.println("第"+n+"个盘子从"+from+"移动"+to);
            //把上面的所有盘子从中间位置移动到目标位置
            hanRota(n-1,in,from,to);
        }
    }
}

3、常见的时间复杂度

    1、常数阶:O(1)
    2、对数阶:O(log2 n)
    3、线性阶:O(n)
    4、线性对数阶:O(n log2 n)
    5、平方阶:O(n²)
    6、立方阶:O(n³)
    7、k次方阶:O(n^k)
    7、指数阶:O(2^n)
    8、阶乘阶:O(n!)
随着问题规模n的不断增大,上述时间复杂度不断增大,算法的执行效率越低。

    3.1、算术表达式

     3.1.1、前缀表达式

        操作符在操作数的前面(是一种没有括号的算术表达式)
        eg:-1+23,等价于1-(2+3)

     3.1.2、中缀表达式

        操作符在操作数的中间(人类最容易识别的算术表达式)
        eg:3+4-5

     3.1.3、后缀表达式

        操作符在操作数的后面(是一种没有括号的算术表达式)
        eg:34+5-,等价于(3+4)-5

    3.2、中缀表达式转换为后缀表达式

    1、初始化两个栈,运算符栈s1,中间结果栈s2
    2、从左到右扫描表达式
    3、遇到操作数压入s2
    4、遇到运算符,比较其与s1栈顶运算符的优先级
        第一步、如果s1栈不是空的,并且栈顶元素不是左括号(,并且优先级比栈顶元素低或相等,就弹出s1栈顶的元素,压入s2栈,递归这一步
        第二步、然后将运算符压入s1栈
    5、遇到括号
        a、遇到左括号(,直接压入s1栈
        b、遇到右括号),依次弹出s1栈顶的元素,压入s2栈,直到遇到左括号为止,然后将这对括号丢掉
    6、重复2~5,扫描完所有表达式
    7、将s1中剩余的运算符压入s2栈

package test.expression;

import java.util.ArrayDeque;
import java.util.Stack;

/**
 * 中缀表达式转换为后缀表达式
 *
 * @createTime 2020年04月14日 9:43.
 */
public class InfixToSuffixTest {
    public static void main(String[] args) {
        String s = "(34.5+4)*2+59*9+24/6+3";
        System.out.println("中缀表达式:");
        System.out.println(s);
        ArrayDeque<String> s2 = infixToSuffix(s);
        System.out.println("后缀表达式:");
        ArrayDeque<String> s3 = new ArrayDeque<>();
        while (!s2.isEmpty()) {
            System.out.print(s2.peek() + "   ");
            s3.add(s2.remove());
        }
    }

    public static ArrayDeque<String> infixToSuffix(String exp) {
        Stack<Character> s1 = new Stack<>();
        ArrayDeque<String> s2 = new ArrayDeque<>();
        char c;
        int lastIndex;
        for (int i = 0; i < exp.length(); i++) {
            if (Character.isDigit(exp.charAt(i))) {//如果是数字
                lastIndex = getLastIndex(exp, i);
                s2.add(exp.substring(i, lastIndex));
                i = lastIndex - 1;
            } else if (isOperator(exp.charAt(i))) {
                while (!s1.isEmpty() && '(' != s1.peek() && operatorCompare(exp.charAt(i), s1.peek()) <= 0) {
                    if (isOperator(s1.peek())) {
                        s2.add(s1.pop().toString());
                    } else {
                        s1.pop();
                    }
                }
                s1.push(exp.charAt(i));
            } else if (exp.charAt(i) == '(') {
                s1.push(exp.charAt(i));
            } else if (exp.charAt(i) == ')') {
                while (s1.peek() != '(') {
                    s2.add(s1.pop().toString());
                }
                if (s1.peek() == '(') {
                    s1.pop();//移除左括号
                }
            } else {
                //其他情况忽略
            }
        }
        while (!s1.isEmpty()) {
            s2.add(s1.pop().toString());
        }
        return s2;
    }

    public static int operatorCompare(char op1, char op2) {
        int r = -1;
        if (op1 == '+' || op1 == '-') {
            r = (op2 == '*' || op2 == '/') ? -1 : 0;
        } else if (op2 == '+' || op2 == '-') {
            r = (op1 == '*' || op1 == '/') ? 1 : 0;
        }
        return r;
    }

    /**
     * 判断是否是操作符号
     *
     * @param c
     * @return
     */
    public static boolean isOperator(char c) {
        return (c == '+' || c == '-' || c == '*' || c == '/') ? true : false;
    }

    /**
     * 获取操作数后面的符号对应在表达式中的索引值
     *
     * @param exp
     * @param start
     * @return
     */
    public static int getLastIndex(String exp, int start) {
        int rIndex = start;
        for (int i = start; i < exp.length(); i++) {
            if (exp.charAt(i) == '.') {
                continue;
            } else if (!Character.isDigit(exp.charAt(i))) {
                rIndex = i;
                break;
            }
        }
        if (rIndex == start) {
            rIndex = exp.length();//当前操作数是最后一个数,后面就没有符号
        }
        return rIndex;
    }

    private static double calculation(double num1, double num2, char a) {
        switch (a) {
            case '+':
                System.out.print("计算:" + num1 + "+" + num2 + "=");
                num2 = num1 + num2;
                System.out.println(num2);
                break;
            case '-':
                System.out.print("计算:" + num1 + "-" + num2 + "=");
                num2 = num1 - num2;
                System.out.println(num2);
                break;
            case '*':
                System.out.print("计算:" + num1 + "*" + num2 + "=");
                num2 = num1 * num2;
                System.out.println(num2);
                break;
            case '/':
                System.out.print("计算:" + num1 + "/" + num2 + "=");
                num2 = num1 / num2;
                System.out.println(num2);
                break;
        }
        return num2;
    }
}

    3.3、抽象数据类型ADT

ADT:Abstract Data Type(抽象数据类型)

抽象:是抽取事物具有的普遍性本质,是对事物的一个概括,是一种思考问题的方式。

抽象数据类型是指一个数学模型及定义在该模型上的一组操作

4、排序算法

资料显示,在数据量小于20时,插入排序具有最好的性能。当大于20时,快速排序具有最好的性能。

    4.1、交换排序

      4.1.1、冒泡排序

        int[] arr = {6, 3, 9, 55, 4, 7, 2};
        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = 0; j < arr.length - 1 - i; j++) {
                if (arr[j] > arr[j + 1]) {
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
        System.out.println(Arrays.toString(arr));

      4.1.2、快速排序

package com.study.test;

import java.util.Arrays;

/**
 * 快速排序
 * Created by Administrator on 2020/4/8 0008.
 */
public class TestQuick {
    public static void main(String[] args){
      int[] arr={0,2,4,1,3,7,9,6,5,10,8};
      quickSort(arr,0,arr.length-1);
      System.out.println(Arrays.toString(arr));
    }
    public static void quickSort(int[] arr,int start,int end){
        if(start<end){
            //把数组中的第0个【开始位置的】元素作为标准数
            int stard = arr[start];
            //记录需要排序的下标
            int low=start;
            int high=end;
            while (low<high){//找比stard大的数、比stard小的数
                while (low<high && stard<=arr[high]){//右边的数字比stard大
                    high--;
                }
                arr[low]=arr[high];//用右边的数替换左边的数
                while (low<high && stard>=arr[low]){//左边的数比stard小
                    low++;
                }
                arr[high]=arr[low];//用左边的数替换右边的数
            }
            arr[low]=stard;//把stard放入数组【这个时候low==high】
            //对比stard小的再排序
            quickSort(arr,start,low-1);
            //对比stard大的再排序
            quickSort(arr,low+1,end);
        }
    }
}

    4.2、插入排序

      4.2.1、直接插入排序

package com.study.test;

import java.util.Arrays;

/**
 * 直接插入排序
 * Created by Administrator on 2020/4/8 0008.
 */
public class TestInsertSort {
    public static void main(String[] args){
        int[] arr={0,2,4,1,3,7,9,6,5,10,8,99,18,28};
        insertSort(arr);
        System.out.println(Arrays.toString(arr));
    }
    public static void insertSort(int[] arr){
        //从第二个元素开始遍历
        for(int i=1;i<arr.length;i++){
            if(arr[i-1]>arr[i]){//如果当前数字比前一个数字小
                int temp =arr[i];//把当前数字缓存起来
                int j;
                //遍历当前数字前面的所有数字
                for (j=i-1;j>=0&&temp<arr[j];j--){
                    arr[j+1]=arr[j];//把前一个数字赋给后一个数字
                }
                arr[j+1]=temp;
            }
        }
    }
}

      4.2.2、希尔排序

遍历的步长(间隔)更高效的取值方式一是用2.2来整除每个间隔

eg:n=100的数组,步长依次是45、20、9、4、1,这比用2整除改善排序效果显著

遍历的步长(间隔)更高效的取值方式二在不小于n/3下间隔序列step*3+1,每次循环后间隔(step-1)/3

逻辑代码如下:

int step=1;
while (step<=arr.length/3){
    step=step*3+1;//找到这个数组的最大间隔
}
//然后用最大步长进行数组遍历
//每一次遍历完后,step=(step-1)/3
while (step>0){
    //.....逻辑比较代码
    step=(step-1)/3;
}

总之,无论是什么间隔序列,间隔最后一定要等于1,也就是最后一趟排序一定是简单的插入排序。

import java.util.Arrays;

/**
 * 希尔排序
 *
 * @createTime 2020年04月16日 10:35.
 */
public class ShellSortTest {
    public static void main(String[] args) {
        int[] arr = {23, 3, 199, 2, 34, 5, 8, -12, 1, 5, 4, 4, 99, 2, 6, 44, 3};//
        shellSort(arr);
        System.out.println(Arrays.toString(arr));
    }

    public static void shellSort(int[] arr) {
        int inner;
        int temp;
        //遍历所有的步长
        double tempStep = arr.length / 2.2;
        int step;
        if (tempStep < 1) {
            step = 1;//保证“最后一定”有一轮间隔为1的排序
        } else {
            step = (int) tempStep;
        }
        while (step > 0) {
            for (int outer = step; outer < arr.length; outer++) {
                //遍历本组中所有的元素
                inner = outer;
                temp = arr[inner];
                while (inner - step >= 0 && temp < arr[inner - step]) {
                    //inner-step==0:表示已经是第一个元素了
                    //temp<arr[inner-step]当前位置元素小于当前位置-步长位置的元素,就将大的元素往后移
                    arr[inner] = arr[inner - step];
                    inner = inner - step;//位置往前移
                }
                arr[inner] = temp;//将小的元素放在最前面的位置
            }
            System.out.println("间隔数是" + step + "时的排序:" + Arrays.toString(arr));
            //计算下一个间隔
            tempStep = step / 2.2;
            if (step != 1 && tempStep < 1) {
                step = 1;//保证“最后一定”有一轮间隔为1的排序
            } else {
                step = (int) tempStep;
            }
        }
    }
}

    4.3、选择排序 

      4.3.1、简单选择排序

import java.util.Arrays;

/**
 * 简单选择排序
 *
 * @createTime 2020年04月09日 10:17.
 */
public class SelectSortTest {
    public static void main(String[] args) {
        int[] arr = {3, 199, 2, 34, 5, 8, -12, 1, 4, 99};
        selectSort(arr);
        System.out.println(Arrays.toString(arr));
    }

    public static void selectSort(int[] arr) {
        //遍历所有的数
        for (int i = 0; i < arr.length; i++) {
            int minIndex = i;//假设i位置是最小的数
            //把i位置后面的所有数依次进行比较
            for (int j = i + 1; j < arr.length; j++) {
                if (arr[j] < arr[minIndex]) {
                    //如果比当前的最小数还小,记录下这个位置
                    minIndex = j;
                }
            }
            if (minIndex != i) {//如果最小数位置不是i,就交换
                int temp = arr[i];
                arr[i] = arr[minIndex];
                arr[minIndex] = temp;
            }

        }
    }
}

      4.3.2、堆排序

    4.4、归并排序 

归并算法的中心是:归并两个已经有序的数组。

eg:归并两个有序数组A、B,就生成了第三个有序数组C。数组C包含数组A和B的所有数据项。

    4.5、基数排序 

5、树

https://blog.csdn.net/pyl574069214/article/details/105556667

6、堆

https://blog.csdn.net/pyl574069214/article/details/105763965

7、图

https://blog.csdn.net/pyl574069214/article/details/105491670

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值