数据结构与算法

算法

是解决特定问题求解步骤的描述:自然语言描述、流程图描述、伪代码描述、程序代码描述

对于1到100的求和问题(有两种解决方案)

  方案1随着N的增大,循环的次数也会增大,也就意味着执行的次数和运行的时间也会增大

方案1
int sum=0;
int N=100;
for(int i=1;i<=N;i++){
	sum+=i;
}

 方案2随着N的增大,其执行次数只有一次,不会随着N的增大而增大

方案2
int N=100;
int sum=(N+1)*N/2;

 算法的好坏如何评判

算法的效率指的是算法的执行时间
度量一个算法的执行时间有:

1.事后统计法:

这种方法主要是通过设计好的程序和数据,利用计算机计时器对不同算法程序的运行时间进行比较,从而确定算法效率的高低。
弊端:
必须事先编好程序,再进行运行,如果程序处理的数据量较大,则会花费大量的时间和精力
时间的比较主要依赖于计算机硬件和软件环境
算法的测试数据设计困难,在数据量较小的时候,不管什么算法其运行时间都是很微小的,相差几乎为零。如果数据量大了,算法的优越性就出来了,但是这样又会耗费时间。

2.事前分析法:

这种方法主要在计算机程序编制前,依据统计方法对算法进行估算。
一个高级程序语言编写的程序在计算机上运行时所消耗的时间取决于下列因素:

1.算法采用的策略、方法
2.编译产生的代码质量
3.问题的输入规模
4.机器执行指令的速度

算法时间复杂度

算法时间复杂度主要探究的是问题输入规模N的数量级,不是算法的具体执行次数。

常数阶O(1)

就是那些无循环无递归、与问题输入规模N无关的逐行执行的代码。

int a = 3;
int b = 4;
int c = a + b;


int a = 1;
int b = 2;
int c = 3;
int d = 4;
int e = 5;
System.out.println(a);
System.out.println(b);
System.out.println(c);
System.out.println(d);
System.out.println(e);

 线性阶O(n)

与问题输入规模有关的,主要是一层循环的代码,多个一层循环可以并列但不能包含。

int N = 10;
for (int i = 1; i <= N; i++) {
    System.out.println(i);
}



int N = 10;
for (int i = 1; i <= N; i++) {
    System.out.println(i);
}
for (int i = 1; i <= N; i++) {
    System.out.println(i);
}

 线性阶O(n+m)

和线性阶O(n)一样,只不过我们有两种数据的输入规模。

int N = 10;
int M = 20;
for (int i = 1; i <= N; i++) {
    System.out.println(i);
}
for (int i = 1; i <= M; i++) {
    System.out.println(i);
}

 平方阶O(n2)

与问题输入规模有关的,主要是二层嵌套循环的代码。

int N = 10;
for (int i = 1; i <= N; i++) {
    for (int j = 1; j <= N; j++) {
        System.out.println(i + j);
    }
}

 平方阶O(nm)

和平方阶O(n2)一样,只不过我们有两种数据输入规模。

int N = 10;
int M = 20;
for (int i = 1; i <= N; i++) {
    for (int j = 1; j <= M; j++) {
        System.out.println(i + j);
    }
}

对数阶O(logn)

与问题输入规模有关的,主要是一层循环迭代或递归的代码。

int count = 1;
int N = 100000;
while (count < N) 
    count = count * 2;

int key = 8;
int min_index = 0;
int max_index = arr.length - 1;
int mid_index = (min_index + max_index) / 2;
while (arr[mid_index] != key) {
    if (key < arr[mid_index]) 
        max_index = mid_index - 1;
    if (arr[mid_index] < key) {
        min_index = mid_index + 1;
    if (min_index > max_index) {
        mid_index = -1;
        break;
    }
    mid_index = (min_index + max_index) / 2;
}

 常见阶的比较

执行次数

非正式术语

12

O(1)

常数阶

5log2n+20

O(logn)

对数阶

2n+3

O(n)

线性阶

2n+3nlog2n+19

O(nlogn)

nlogn

3n2+2n+1

O(n2)

平方阶

2n

O(2n)

指数阶

n!

O(n!)

阶乘阶

6n3+2n2+3n+4

O(n3)

立方阶

数据结构---逻辑结构

线性结构: 线性结构中的数据元素之间是一对一的关系
树形结构: 树形结构中的数据元素之间存在一种一对多的层次关系
图形结构: 图形结构的数据元素是多对多的关系

数据结构---物理结构

顺序存储结构: 开辟一组连续的空间存储数据 ,通常用数组来实现,数组中空间本身是连续的,保证了数据之间的关系
链式存储结构: 开辟一组随机的空间存储数据 ,通常用节点来实现,节点不仅要存储数据还要存储下一个节点的位置以保证数据之间的关系

 静态数组与动态数组

内置数组的特点和缺点

Java内置数组的特点:
数组的长度一旦确定则不可更改
数组只能存储同一类型的数据
数组中每个存储空间地址是连续且相等的
数组提供角标的方式访问元素

Java内置数组的缺点:
长度不可变,容量不够用怎么办
地址连续且提供角标访问很快,但增删元素怎么办
数组只有length这个属性,没有其他的方法

线性表的定义

零个或多个元素的有限序列

𝑎 i −1    𝑎 ia_(i-1)  是 a_i 直接前驱
𝑎 i +1    𝑎 ia_(i+1)  是 a_i 直接后继
除了第 1 个元素 𝑎 1a_1 之外,其他元素都有唯一的直接前驱
除了第 n 个元素 𝑎 𝑛a_n 之外,其他元素都有唯一的直接后继
n 表示线性表的长度,当 n=0 时,称为空表

 List接口的定义

接下来我们用代码来实现一下线性表中的List接口

package org.oracle.jiekou;

import java.util.Comparator;

public interface List<E> extends Iterable<E>{
    //默认在表尾添加一个元素
    public void add(E element);
    //在指定角标处添加元素
    public void add(int index, E element);
    //删除指定元素
    public void remove(E element);
    //删除指定角标处元素 并返回原先的值
    public E remove(int index);
    //获取指定角标处的元素
    public E get(int index);
    //修改指定角标index处的值element 并返回原先的值
    public E set(int index, E element);
    //获取线性表中的元素个数
    public int size();
    //查看元素第一次出现角标的位置(从左到右)
    public int indexOf(E element);
    //判断是否包含元素
    public boolean contains(E element);
    //判断线性表是否为空
    public boolean isEmpty();
    //清空线性表
    public void clear();
    //按照比较器的内容进行排序
    public void sort(Comparator<E> c);
    //获取了线性表 原线性表中(fromIndex,toIndex)这个部分
    public List<E> subList(int fromIndex,  int toIndex);
}

线性表的实现ArrayList 

ArrayList就是线性结构顺序存储方式的具体实现,称为线性表

创建ArrayList类实现List接口,定义相关成员属性和构造函数

add(int index, E element) 添加
resize(int newLength) 扩容
remove(int index)移动
resize(int newLength) 缩容
indexOf(E element) 进行比较
sort(Comparator<E> c)排序(比较器)
subList(int fromIndex, int toIndex)list.subList(int fromIndex, int toIndex)  返回list中指定下标的元素组成的list集合,左闭右开(包括fromIndex元素,不包括toIndex).
Iterator<E> iterator()迭代器,实现了Iterable<E>的类型,均可以通过iterator()获得该容器上的迭代器Iterator<E>实例

package org.oracle.p2;
import org.oracle.jiekou.List;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
//自定义的线性表的顺序存储方式
public class ArrayList<E> implements List<E> {
    //数组的容器 data.length 指的是当前数组的容量
    private E[] data;
    //元素的个数 size == 0 线性表为空 size == date.length 线性表满了
    //size 新元素默认从尾部添加时要去角标
    private int size;
    //默认容量
    private static int DEFAULT_CAPACITY = 10;
    //默认构造函数;创建一个默认容量为10的线性表
    public ArrayList(){
        data = (E[]) new Object[DEFAULT_CAPACITY];
        size = 0;
    }
    //指定默认容量的构造函数:创建一个指定容量的线性表
    public ArrayList(int capacity){
        if (capacity <= 0){
            throw new IllegalArgumentException("capacity must > 0");
        }
        DEFAULT_CAPACITY = capacity;
        data = (E[]) new Object[DEFAULT_CAPACITY];
        size = 0;
    }
    //指定数组的构造函数:传入一个数组 将该数组封装成一个线性表
    //[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ]
    public ArrayList(E[] arr){
        if (arr == null || arr.length == 0){
            throw new IllegalArgumentException("arr can not be null");
        }
        data = (E[]) new Object[DEFAULT_CAPACITY];
        for (int i = 0; i < arr.length; i++){
            add(arr[i]);
        }
    }
    @Override
    public void add(E element) {
        add(size, element);

    }
    @Override
    public void add(int index, E element) {
        if (index < 0 || index > size){
            throw new IllegalArgumentException("add index out of range");
        }
        //判断线性表是否是满状态
        if (size == data.length){
            resize(2 * data.length);
        }
        //向后移动元素
        for (int i = size - 1; i >= index; i-- ){
            data[i + 1] = element;
        }
        //将新元素插入到指定位置
        data[index] = element;
        size++;
    }
    //扩容/缩容 操作 不应该向外界提供
    private void resize(int newLen) {
        E[] newDate = (E[]) new Object[newLen];
        for (int i = 0; i < size; i++){
            newDate[i] = data[i];
        }
        data = newDate;
    }
    @Override
    public void remove(E element) {
        //删除指定元素 只删除一次 && 删除所有的指定元素
        int index = indexOf(element);
        if (index != -1){
            remove(index);
        }

    }

    @Override
    public E remove(int index) {
        if (index < 0 || index >= size){
            throw new IllegalArgumentException("remove index out of range");
        }
        //先保存要删除的值
        E ret = data[index];

        //移动元素
        for (int i = index + 1; i < size; i++){
            data[i - 1] = data[i];
        }
        size--;

        //什么时候缩容
        //1.有效元素是容量的四分之一
        //2.当前容量不得小于等于默认容量
        if (size == data.length / 4 && data.length > DEFAULT_CAPACITY){
            resize(data.length / 2);
        }
        return ret;
    }
    @Override
    public E get(int index) {
        if (index < 0 || index >= size){
            throw new IllegalArgumentException("get index out of range");
        }
        return data[index];
    }

    @Override
    public E set(int index, E element) {
        if (index < 0 || index >= size){
            throw new IllegalArgumentException("set index out of range");
        }
        E ret = data[index];
        data[index] = element;
        return ret;
    }

    @Override
    public int size() {
        return size;
    }

    //额外添加一个函数 获取线性表的容量
    private int getCapacity(){
        return data.length;
    }

    @Override
    public int indexOf(E element) {
        /*
         * == 比的是啥?主要看等号的两边是啥
         * == 两边是基本数据类型的话 比的是值
         *     byte short int long
         *     float double
         *     char boolean
         * == 两边是引用数据类型的话 比的是地址
         *    数组 字符串 其他类的对象
         */
        for (int i = 0; i < size; i++){
            if (data[i].equals(element)){
                return i;
            }
        }
        return -1;
    }
    @Override
    public boolean contains(E element) {
        return indexOf(element) != -1;
    }
    @Override
    public boolean isEmpty() {
        return size == 0;
    }
    @Override
    public void clear() {
        data = (E[]) new Object[DEFAULT_CAPACITY];
        size = 0;
    }
    @Override
    public void sort(Comparator<E> c) {
        if (c == null){
            throw new IllegalArgumentException("comparator can not be null");
        }
        for (int i = 1; i < size; i++){
            E e = data[i];
            int j = 0;
            for (j = i; j > 0 && c.compare(data[j - 1],e) > 0; j-- ){
                data[j] = data[j - 1];
            }
            data[j] = e;
        }
    }
    @Override
    public List<E> subList(int fromIndex, int toIndex) {
        if (fromIndex < 0 || toIndex >= size || fromIndex > toIndex){
            throw new IllegalArgumentException("must 0 <= fromIndex <= toIndex <= size - 1");
        }
        ArrayList<E> list = new ArrayList<>();
        for (int i = fromIndex; i <= toIndex; i++){
            list.add(data[i]);
        }
        return list;
    }
    @Override
    public boolean equals(Object o) {
        //1.判空
        if (o == null){
            return false;
        }
        //2.判自己
        if (this == o){
            return true;
        }
        //3.判类型
        if (o instanceof ArrayList){
            //4.按照自己的逻辑进行比较
            ArrayList<E> other = (ArrayList<E>) o;
            //5.先比较有效元素的个数
            if (this.size != other.size){
                return false;
            }
            //6.有效元素个数相等的情况下 逐个去比较元素
            for (int i = 0; i < size; i++){
                if (!data[i].equals(other.data[i])){
                    return false;
                }
            }
            return true;
        }
         return false;
    }
    /*
     * [1, 2, 3, 4, 5, 6]
     * []
     * Arrays.toString(arr);
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append('[');
        if (isEmpty()){
            sb.append(']');
        }else{
            for (int i = 0; i < size; i++){
                sb.append(data[i]);
                if (i == size - 1){
                    sb.append(']');
                }else{
                    sb.append(',');
                    sb.append(' ');
                }
            }
        }
        return sb.toString();
    }
    //获取当前这个数据结构/容器 的 迭代器
    //通过迭代器对象 更方便挨个取出每一个元素
    //同时 实现了Iterable 可以让当前的数据结构/容器 被foreach循环遍历
    @Override
    public Iterator<E> iterator() {

        return new ArrayListIterator();
    }
    //创建一个属于Arraylist的迭代器
    class ArrayListIterator implements Iterator<E>{
        private int cur = 0;
        @Override
        public boolean hasNext() {//判断是否有下一个元素
            return cur < size;
        }
        @Override
        public E next() {//如果有下一个元素 则把当前元素返回 并移到下一个元素
            return data[cur++];
        }
    }

}

ArrayList(E[] arr)的问题

data不能直接引用外部传入的数组arr,否则外部对arr的修改会引起ArrayList内部的一些问题

TestArrayList的建立

package org.oracle.Test;

import org.oracle.p2.ArrayList;

import java.util.Comparator;
import java.util.Iterator;
import java.util.Random;

public class TestArrayList {
    public static void main(String[] args){
        ArrayList<Integer> list = new ArrayList<>();
        System.out.println(list);
        Random random = new Random();
        for (int i = 0; i < 10; i++){
            list.add(random.nextInt(100));
        }
        System.out.println(list);

        for (int i = 0; i < 10; i++){
            //list.add(0, i);
            list.add(i);
            //System.out.println(list);
        }
        System.out.println(list);


        list.sort(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });
        System.out.println(list);

        for (Integer num : list){
            System.out.println(num);
        }
        Iterator<Integer> it = list.iterator();
        while (it.hasNext()){
            System.out.println(it.next() + " ");
        }
    }
}

 测试结果

 栈的定义

栈是限定仅在表尾进行插入和删除操作线性表

我们把允许插入和删除的一端称为 栈顶( top ),另一端称为栈底( bottom
不含任何数据元素的栈称为 空栈
栈又称为后进先出( Last In First Out )的线性表,简称 LIFO 结构
栈本身是一个线性表,其数据元素具有线性关系,只不过它是一种特殊的线性表而已
栈的插入操作,叫作进栈,也称压栈、入栈

  栈的删除操作,叫作出栈,也称弹栈

Stack栈接口的定义 

同样栈可以顺序存储实现也可以链表存储实现,所以将共性抽取定义出Stack接口

package org.oracle.jiekou;

public interface Stack<E> extends Iterable<E> {
    public int size();
    public boolean isEmpty();
    //入栈 进栈一个元素 在线性表的表尾添加一个元素
    public void push(E element);
    //出栈 弹出一个元素 在线性表的表尾删除一个元素
    public E pop();
    //查看当前栈顶元素 并不是移除 查看线性表中最后一个元素
    public E peek();
    public void clear();
}

 ArrayStack类

package org.oracle.p2;
import org.oracle.jiekou.Stack;
import java.util.Iterator;
import java.util.Objects;
public class ArrayStack<E> implements Stack<E> {
    private ArrayList<E> list;
    public ArrayStack(){
        list = new ArrayList<>();
    }
    public ArrayStack(int capacity){
        list = new ArrayList<>(capacity);
    }
    @Override
    public int size() {
        return list.size();
    }
    @Override
    public boolean isEmpty() {
        return list.isEmpty();
    }
    @Override
    public void push(E element) {
        list.add(element);

    }
    @Override
    public E pop() {
        return list.remove(list.size() - 1);
    }
    @Override
    public E peek() {
        return list.get(list.size() - 1);
    }
    @Override
    public void clear() {
        list.clear();
    }

    @Override
    public Iterator<E> iterator() {
        return list.iterator();
    }
    @Override
    public String toString() {
        return list.toString();
    }
    @Override
    public boolean equals(Object o) {
        if (o == null){
            return false;
        }
        if (this == o){
            return true;
        }
        if (o instanceof ArrayStack){
            ArrayStack other = (ArrayStack) o;
            return this.list.equals(other.list);
        }
        return false;
    }
    @Override
    public int hashCode() {
        return Objects.hash(list);
    }
}

 中缀表达式

是一个通用的 算术或逻辑公式表示方法 , 操作符是以中缀形式处于操作数的中间
(例:
3 + 4
前缀表达式 (例: + 3 4 )或 后缀表达式 (例: 3 4 + )相比,中缀表达式不容易被计算机解析,但仍被许多程序语言使用,因为它符合人们的普遍用法。
与前缀或后缀记法不同的是,中缀记法中括号是必需的。

格式化表达式

如果原封不动的遍历表达式字符串(10+20/2*3)/2+8将得到如下结果:

[(, 1, 0, +, 2, 0, /, 2, *, 3, ), /, 2, +, 8]

最好的分隔结果是:

[(, 10, +, 20, /, 2, *, 3, ), /, 2, +, 8]

思路:将字符串格式化为如下情形,再进行分隔即可

#(#10#+#20#/#2#*#3#)#/#2#+#8

接下来我们将以(10+20/2*3)/2+8为例进行代码实现

package org.oracle.p2;
//中缀表达式计算器
public class InfixCalculator {
    public static void main(String[] args){
        String expression = "(10+20/2*3)/2+8";
        try{
            int result = evaluateExpression(expression);
            System.out.println(result);
        }catch(Exception e){
            e.printStackTrace();
            System.out.println("Wrong expression :" + expression);
        }
//        int result = evaluateExpression(expression);
//        expression = insertBlanks(expression);
//        String[] tokens = expression.split(" ");
//        for (int i = 0; i < tokens.length; i++){
//            System.out.println("""+ tokens[i] + """);
//        }

    }
    private static int evaluateExpression(String expression) {
        //需要两个辅助栈结构
        ArrayStack<Character> operatorStack = new ArrayStack<>();
        ArrayStack<Integer> numberStack = new ArrayStack<>();

        //格式化表达式
        expression = insertBlanks(expression);
        String[] tokens = expression.split(" ");
        for (String token : tokens) {  //token == tokens[i]
            //过滤空串
            if (token.length() == 0) {
                continue;
                //遍历到+-号
                //如果之前是别的+-*/ 需要弹栈计算
            } else if (token.equals("+") || token.equals("-")) {
                while (!operatorStack.isEmpty() && (operatorStack.peek() == '+' || operatorStack.peek() == '-' || operatorStack.peek() == '*' || operatorStack.peek() == '/')) {
                    //如果之前是别的+-*/ 需要弹栈计算
                    processAnOperator(numberStack, operatorStack);
                }
                //如果操作符栈为空 或者 不为空 但栈顶为(
                operatorStack.push(token.charAt(0));

                //遍历到 * / 号
            } else if (token.equals("*") || token.equals("/")) {
                while (!operatorStack.isEmpty() &&  (operatorStack.peek() == '*' || operatorStack.peek() == '/')){
                    //如果之前是别的 * / 则需要弹栈 并计算
                    processAnOperator(numberStack, operatorStack);
                }
                //如果操作符栈为空 或者 不为空但栈顶为(
                operatorStack.push(token.charAt(0));
                //遍历到(
            }else if (token.equals("(")){
                operatorStack.push(token.charAt(0));

                //遍历到 )
            }else if (token.equals(")")){
                //只要操作符栈的栈顶不是左括号就挨个弹栈计算
                while (operatorStack.peek() != '('){
                    processAnOperator(numberStack, operatorStack);
                }
                //最后 清除左括号
                operatorStack.pop();

                //遍历数字
            } else {
                numberStack.push(Integer.parseInt(token));
            }
        }
            //处理最后面的操作符
        while (!operatorStack.isEmpty()){
            processAnOperator(numberStack, operatorStack);

        }
        return numberStack.pop();
    }
    //操作符区域弹栈一个元素 数字栈弹栈两个数字 进行计算 并将新的结果进栈到数字栈
    private static void processAnOperator(ArrayStack<Integer> numberStack, ArrayStack<Character> operatorStack) {
        char op = operatorStack.pop();
        int num1 = numberStack.pop();
        int num2 = numberStack.pop();
        //num2 op num1
        if (op == '+'){
            numberStack.push(num2 + num1);
        }else if (op == '-'){
            numberStack.push(num2 - num1);
        }else if (op == '*'){
            numberStack.push(num2 * num1);
        }else if (op == '/'){
            numberStack.push(num2 / num1);
        }
    }

    //对原表达式进行格式化处理 给所有的非数字
    private static String insertBlanks(String expression) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < expression.length(); i++){
            char c = expression.charAt(i);
            if (c == '(' || c== ')' || c == '+' || c == '-' || c == '/' || c == '*'){
                sb.append(' ');
                sb.append(c);
                sb.append(' ');

            }else{
                sb.append(c);
            }
        }
        return sb.toString();
    }
}

这就是我本次的分享,谢谢大家的观看

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SereinWen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值