数据结构---01

数据结构和算法关系

  • 数据结构data结构是一门研究组织数据方式的学科,有了编程语言就有了数据结构,编写出更漂亮更有效率的代码
  • 要学习数据结构要多考虑生活中的问题,用程序解决
  • 程序 = 数据结构 + 算法
  • 数据结构是算法的基础

线性结构和非线性结构

线性结构

数组 队列 链表 栈

线性结构作为最常用的数据结构,特点:数据元素之间一对一的线性关系

线性结构有两种不同的存储结构,顺序结构和链式结构

顺序结构线性表是顺序表,顺序表存储元素连续

链式存储线性表称为链表,链表中的存储元素不一定连续,元素节点中存放元素和相邻元素地址信息

非线性结构

二维数组,多维数组,广义表,树结构,图结构

稀疏数组

基本介绍

一个数组大部分元素是0,或者同一个值的数组,使用稀疏数组来保存该数组

稀疏数组处理:

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

二维数组转稀疏数组思路:

  1. 遍历原始二维数组,记录有效数据个数
  2. 根据sum创建sparseArr int [sum] [ 3 ]
  3. 将二维数组的有效数据存入稀疏数组

稀疏数组转二维数组思路:

  1. 读取稀疏数组第一行,根据数据创建原始二维数组,chessArr2 = int [11] [11]
  2. 读取稀疏数组后几行数据,赋给原始二维数组
package com.fu.datastruct;

public class SparseArr {
    public static void main(String[] args) {
        int sum = 0;
        //输出二维数组
        int[][] chessArr1 = new int[11][11];
        chessArr1[3][3] = 4;
        chessArr1[4][4] = 5;
        for (int[] c1 : chessArr1) {
            for (int c2 : c1) {
                System.out.printf("%d\t",c2);
                if (c2 != 0){
                    sum+=1;
                }
            }
            System.out.println();
        }
        System.out.println("有效值:"+sum);
        //二维数组转稀疏数组
        System.out.println("输出稀疏数组~~");
        int[][] sparseArr = new int[sum + 1][3];
        sparseArr[0][0] = 11;
        sparseArr[0][1] = 11;
        sparseArr[1][1] = sum;
        int count = 0;
        for (int i = 0; i < chessArr1.length; i++) {
            for (int j = 0; j < chessArr1[i].length; j++) {
                if (chessArr1[i][j] != 0){
                    count++;
                    sparseArr[count][0] = i;
                    sparseArr[count][1] = j;
                    sparseArr[count][2] = chessArr1[i][j];
                }
            }
        }
        for (int[] s1 : sparseArr) {
            for (int s2 : s1) {
                System.out.printf("%d\t",s2);
            }
            System.out.println();
        }
        System.out.println("输出二维数组~~");
        int[][] chessArr2 = new int[sparseArr[0][0]][sparseArr[0][1]];
        for (int i = 1; i < sparseArr.length; i++) {
            chessArr2[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2];
        }
        for (int[] c1 : chessArr2) {
            for (int c2 : c1) {
                System.out.printf("%d\t",c2);
            }
            System.out.println();
        }
    }
}

队列

队列介绍

  1. 队列是一个有序列表,可以用数组或链表实现
  2. 先入先出

数组模拟队列

  1. 队列本身是有序队列,使用数组结构存储队列数据,则队列数组声明其中maxSIze是该队列最大容量
  2. 队列输入输出,输入从前后端处理,front rear 记录队列前后端下标,front随数据输出改变,rear随输入改变
package com.fu.datastruct.queue;


import java.util.Scanner;

public class ArrayQueueDemo {
    public static void main(String[] args) {
        ArrayQueue queue = new ArrayQueue(3);
        char key = ' ';
        Scanner scanner = new Scanner(System.in);
        boolean loop = true;
        while(loop){
            System.out.println("s(show) 显示队列");
            System.out.println("e(exit) 退出队列");
            System.out.println("a(add) 添加数据到队列");
            System.out.println("g(get) 从队列取出数据");
            System.out.println("h(head) 查看队列头数据");
            key = scanner.next().charAt(0);
            switch (key){
                case 'a':
                    int i = scanner.nextInt();
                    queue.addQueue(i);
                    break;
                case 'g':
                    try {
                        int r = queue.getQueue();
                        System.out.println("取出的数是:" + r);
                    } catch (Exception e) {
                        e.getMessage();
                    }
                    break;
                case 's':
                    queue.showQueue();
                    break;
                case 'h':
                    try {
                        int h = queue.headQueue();
                        System.out.println("取出的数是:" + h);
                    } catch (Exception e) {
                        e.getMessage();
                    }
                    break;
                case 'e':
                    scanner.close();
                    loop = false;
                    break;
                default:
                    break;


            }
    }
        System.out.println("退出程序~~");
}}
class ArrayQueue{
    private int maxSize;
    private int front;
    private int rear;
    private int []arr;

    public ArrayQueue(int arrMaxSize) {
        maxSize = arrMaxSize;
        front = -1;
        rear = -1;
        arr = new int[maxSize];
    }
    public boolean isFull(){
        return rear == maxSize -1;
    }
    public boolean isEmpty(){
        return rear == front;
    }
    public void addQueue(int n){
        if (isFull()){
            System.out.println("队列已满,无法加入");
            return;
        }
        rear++;
        arr[rear] = n;
    }
    public int getQueue(){
        if (isEmpty()){
            throw new RuntimeException("队列为空,无法取出头数据");
        }
        front++;
        return arr[front];
    }
    public void showQueue(){
        if (isEmpty()){
            System.out.println(("队列为空,无法取出头数据"));
        }
        for (int i = 0; i < arr.length; i++) {
            System.out.printf("arr[%d]=%d\n",i,arr[i]);
        }
    }
    public int headQueue(){
        if (isEmpty()){
            throw new RuntimeException("队列为空,无法取出头数据");
        }
        return arr[front + 1];
    }
}

问题分析并优化:

  1. 目前数组使用一次就不能用,没有达到复用的效果
  2. 将这个数组使用算法,改进一个环形的数组 %

数组模拟环形队列

  1. front变量的含义调整:front指向队列第一个元素,arr[front]就是队列第一个元素,front初始值是0
  2. rear变量含义调整:rear指向队列最后一个元素后一个位置,因为希望空出一个空间作为约定,rear初始值为0
  3. 队列满是,条件是(rear + 1) % maxSize = front [满]
  4. 对队列为空条件,rear == front 空
  5. 我们这样分析,队列有效数据个数 [rear + maxSize - front] % maxSize
package com.fu.datastruct.queue;

import java.util.Scanner;

public class CircleQueueDemo {
    public static void main(String[] args) {
        CircleQueue queue = new CircleQueue(4);
        char key = ' ';
        Scanner scanner = new Scanner(System.in);
        boolean loop = true;
        while(loop){
            System.out.println("s(show) 显示队列");
            System.out.println("e(exit) 退出队列");
            System.out.println("a(add) 添加数据到队列");
            System.out.println("g(get) 从队列取出数据");
            System.out.println("h(head) 查看队列头数据");
            key = scanner.next().charAt(0);
            switch (key){
                case 'a':
                    int i = scanner.nextInt();
                    queue.addQueue(i);
                    break;
                case 'g':
                    try {
                        int r = queue.getQueue();
                        System.out.println("取出的数是:" + r);
                    } catch (Exception e) {
                        e.getMessage();
                    }
                    break;
                case 's':
                    queue.showQueue();
                    break;
                case 'h':
                    try {
                        int h = queue.headQueue();
                        System.out.println("取出的数是:" + h);
                    } catch (Exception e) {
                        e.getMessage();
                    }
                    break;
                case 'e':
                    scanner.close();
                    loop = false;
                    break;
                default:
                    break;


            }
        }
        System.out.println("退出程序~~");
    }}
class CircleQueue{
    private int maxSize;
    private int front;
    private int rear;
    private int []arr;

    public CircleQueue(int arrMaxSize) {
        maxSize = arrMaxSize;
        arr = new int[maxSize];
    }
    public boolean isFull(){
        return (rear + 1) % maxSize == front;
    }
    public boolean isEmpty(){
        return rear == front;
    }

    public void addQueue(int n){
        if (isFull()){
            System.out.println("队列已满,无法加入");
            return;
        }
        arr[rear] = n;
        rear = (rear + 1)%maxSize;
    }
    public int getQueue(){
        if (isEmpty()){
            throw new RuntimeException("队列为空,无法取出头数据");
        }
        int value = arr[front];
        front = (front + 1 )%maxSize;
        return value;
    }
    public void showQueue(){
        if (isEmpty()){
            System.out.println(("队列为空,无法取出头数据"));
        }
        for (int i = front; i < front + size(); i++) {
            System.out.printf("arr[%d]=%d\n",i % maxSize,arr[i%maxSize]);
        }
    }
    public int size(){
        return (rear - front + maxSize) % maxSize;
    }
    public int headQueue(){
        if (isEmpty()){
            throw new RuntimeException("队列为空,无法取出头数据");
        }
        return arr[front];
    }


}

单链表

链表介绍

  1. 链表是以节点的方式存储
  2. 每个节点包含data域 next域 指向下一个节点
  3. 各个节点不一定是连续存储
  4. 链表分带头结点的链表和没有头节点的链表,根据需求确定

增删改插代码实现

package com.fu.datastruct.queue;

public class SingleLinkedListDemo {
    public static void main(String[] args) {
        HeroNode h1 = new HeroNode(1, "宋江", "及时雨");
        HeroNode h2 = new HeroNode(2, "卢俊义", "玉麒麟");
        HeroNode h3 = new HeroNode(3, "吴用", "智多星");
        HeroNode h4 = new HeroNode(4, "林冲", "豹子头");
        SingleLinkedList s = new SingleLinkedList();
        s.addByOrder(h1);
        s.addByOrder(h4);
        s.addByOrder(h3);
        s.addByOrder(h2);

        s.list();
        System.out.println();
        s.del(1);
        s.update(new HeroNode(2,"晁盖","托塔天王"));
        s.list();
    }

}
class SingleLinkedList{
    private HeroNode head = new HeroNode(0,"","");
    public void add(HeroNode heroNode){//添加一个heroNode
        HeroNode temp = head;
        while(true){
            if (temp.next == null)break;
            temp = temp.next;
        }
        temp.next = heroNode;
    }
    public void list(){
        if (head == null) {
            System.out.println("链表为空");
            return;
        }
        HeroNode temp = head.next;
        while (true){
            if (temp == null){
                return;
            }
            System.out.println(temp);
            temp = temp.next;
        }
    }
    public void addByOrder(HeroNode heroNode){
        HeroNode temp = head;
        boolean flag = false;
        while(true){
            if (temp.next == null)break;
            else if (temp.next.no > heroNode.no)break;
            else if (temp.next.no == heroNode.no){
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if (flag){
            System.out.println("准备的英雄编号" + heroNode.no + "已经存在了,不用在插入了");
        }else {
            heroNode.next = temp.next;
            temp.next = heroNode;
        }
    }
    public void update(HeroNode newHero){
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }
        HeroNode temp = head.next;
        boolean flag = false;
        while(true){
            if (temp == null){
                break;
            }
            else if (temp.no == newHero.no){
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if(flag){
            temp.name = newHero.name;
            temp.nickname = newHero.nickname;
        }
        else System.out.println("没有找到" + newHero.no + "编号英雄,不能修改");
    }

    public void del(int no){
        HeroNode temp = head;
        boolean flag = false;
        while (true){
            if (temp.next == null) {
                break;
            }
            else if (temp.next.no == no){
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if (flag){
            temp.next = temp.next.next;
        }else System.out.println("没有对应下标英雄,无法删除");



    }


}
class HeroNode{
    public int no;
    public String name;
    public String nickname;
    public  HeroNode next;

    public HeroNode(int no, String name, String nickname) {
        this.no = no;
        this.name = name;
        this.nickname = nickname;
    }

    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickname='" + nickname + '\'' +
                '}';
    }
}


双向链表

单链表缺点

  1. 单向链表,查找方向只有一个,双向链表可以向前向后查找
  2. 单向链表不能自我删除,需要靠辅助节点,双向链表可以自我删除,单链表删除时节点,总是找到temp,temp时待删除节点的前一个节点
package com.fu.datastruct.queue;

public class DoubleLinkedListDemo {
    public static void main(String[] args) {
        HeroNode2 h1 = new HeroNode2(1, "宋江", "及时雨");
        HeroNode2 h2 = new HeroNode2(2, "吴用", "智多星");
        HeroNode2 h3 = new HeroNode2(3, "公孙胜", "入云龙");
        DoubleLinkedList d = new DoubleLinkedList();
        d.add(h1);
        d.add(h2);
        d.add(h3);
        d.list();
        d.add(new HeroNode2(4 , "戴宗","神行太保"));
        d.list();
    }
}
class DoubleLinkedList{
    private HeroNode2 head = new HeroNode2(0,"","");
    public void add(HeroNode2 heroNode){//添加一个heroNode
        HeroNode2 temp = head;
        while(true){
            if (temp.next == null)break;
            temp = temp.next;
        }
        temp.next = heroNode;
        heroNode.pre = temp;
    }
    public void list(){
        if (head == null) {
            System.out.println("链表为空");
            return;
        }
        HeroNode2 temp = head.next;
        while (true){
            if (temp == null){
                return;
            }
            System.out.println(temp);
            temp = temp.next;
        }
    }
    public void update(HeroNode2 newHero){
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }
        HeroNode2 temp = head.next;
        boolean flag = false;
        while(true){
            if (temp == null){
                break;
            }
            else if (temp.no == newHero.no){
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if(flag){
            temp.name = newHero.name;
            temp.nickname = newHero.nickname;
        }
        else System.out.println("没有找到" + newHero.no + "编号英雄,不能修改");
    }
    public void del(int no){
        HeroNode2 temp = head.next;
        if (head.next == null){
            System.out.println("链表为空~");
        }
        boolean flag = false;
        while (true){
            if (temp == null) {
                break;
            }
            else if (temp.no == no){
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if (flag){
            temp.pre.next = temp.next;
            if (temp.next != null) temp.next.pre = temp.pre;

        }else System.out.println("没有对应下标英雄,无法删除");
    }
}
class HeroNode2{
    public int no;
    public String name;
    public String nickname;
    public HeroNode2 next;
    public HeroNode2 pre;

    public HeroNode2(int no, String name, String nickname) {
        this.no = no;
        this.name = name;
        this.nickname = nickname;
    }

    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickname='" + nickname + '\'' +
                '}';
    }
}

约瑟夫问题(环形链表)

用不带头结点的循环链表来处理,先构成有n个节点单循环链表,由k节点从1开始计数,记到m对应节点删除,从被删除节点下一个节点从1开始计数,直到最后一个节点从链表删除

package com.fu.datastruct.queue;

public class Joseph {
    public static void main(String[] args) {
        CircleSingleLinkedList c = new CircleSingleLinkedList();
        c.addBoy(5);
        c.showBoy();
        c.countBoy(1,2,5);
    }

}
class CircleSingleLinkedList{
    private Boy first = null;
    public void addBoy(int nums){
        if (nums < 2) {
            System.out.println("nums的值不正确");
            return;
        }
        Boy curBoy = null;//辅助变量
        for (int i = 1; i <= nums ; i++) {
            //根据编号创建小孩变量
            Boy boy = new Boy(i);
            if (i == 1){
                first = boy;
                first.setNext(first);//构成环
                curBoy = first;
            }else {
                curBoy.setNext(boy);
                boy.setNext(first);
                curBoy = boy;
            }


        }

    }
    public void showBoy(){
        if (first == null){
            System.out.println("这是空链表,无法遍历");
            return;
        }
        Boy curBoy = first;
        while (true){
            System.out.println("小孩编号:" + curBoy.getNo());
            if (curBoy.getNext() == first){
                break;
            }
            curBoy = curBoy.getNext();
        }
    }

    /**
     *
     * @param startNo  表示从第几个小孩开始数数
     * @param countNum  表示数几下
     * @param nums       表示最初几个小孩在圈内
     */
    public void countBoy(int startNo,int countNum,int nums){
        if (first == null|| startNo < 1 || startNo > nums){
            System.out.println("参数输入错误,请重新输出");
            return;
        }
        Boy helper = first;
        while (true){
            if (helper.getNext() == first){//最后
                break;
            }
            helper = helper.getNext();
        }
        for (int i = 0; i < startNo - 1; i++) {
            first = first.getNext();
            helper = helper.getNext();
        }
        while(true){
            if (helper == first){//圈中只有一人
                break;
            }
            else {
                for (int i = 0; i < countNum - 1; i++) {
                    first = first.getNext();
                    helper = helper.getNext();
                }
                System.out.println("小孩" + first.getNo() + "出圈");
                first = first.getNext();
                helper.setNext(first);
            }

        }
        System.out.println("最后留在圈中小孩编号" + first.getNo());




    }



}
class Boy{
    private int no;
    private Boy next;

    public Boy(int no) {
        this.no = no;
    }

    public Boy(int no, Boy next) {
        this.no = no;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public Boy getNext() {
        return next;
    }

    public void setNext(Boy next) {
        this.next = next;
    }
}

  1. 1. 栈的英文stack
    2. 栈是**先入后出**有序列表
    3. 栈是限制线性表中元素插入和删除只能在线性表一端进行的特殊线性表,允许插入和删除的一端为变化的一端,为变化的一端,称为**栈顶**,另一端为固定的一端,称为**栈底**
    4. 根据栈的定义可知,最先放入栈的元素在栈底,最后放入元素在栈顶,删除元素则相反,最后放入元素最先删除,最先放入元素最后删除
    

应用场景

  1. 子程序调用:在跳往子程序前,先将下一个指令的地址存在堆栈中,直到子程序执行完后在将地址取出,以回到原来程序中
  2. 处理递归调用:和子程序调用类似,除了储存下一个指令地址外,也将参数,区域变量等数据存入堆栈中
  3. 表达式转换和求值
  4. 二叉树遍历
  5. 图形深度优先搜索法
package com.fu.datastruct.stack;

import com.sun.org.apache.bcel.internal.generic.IF_ACMPEQ;

import java.util.Scanner;
import java.util.Stack;

public class ArrayStackDemo {
    public static void main(String[] args) {
        ArrayStack stack = new ArrayStack(4);
        boolean loop = true;
        String key = "";
        Scanner scanner = new Scanner(System.in);
        while(loop){
            System.out.println("push 入栈操作");
            System.out.println("pop 出栈操作");
            System.out.println("list 打印操作");
            System.out.println("exit 离开操作");
            System.out.println("请输入你的操作:");
            key = scanner.next();
            switch (key){
                case "push":
                    System.out.println("请输入入栈的值:");
                    int value = scanner.nextInt();
                    stack.push(value);
                    break;
                case "pop":
                    stack.pop();
                    break;
                case "list":
                    stack.list();
                    break;
                case "exit":
                    scanner.close();
                    loop = false;
                    break;
                default:
                    break;
            }

        }
        System.out.println("退出程序~~");
    }
}

class ArrayStack{
    private int[] stack;
    private int maxSize;
    private int top = -1;

    //构造器
    public ArrayStack(int maxSize) {
        this.maxSize = maxSize;
        stack = new int[this.maxSize];
    }

    //栈满
    public boolean isFull(){
        return top == maxSize - 1;
    }
    //栈空
    public boolean isEmpty(){
        return top == -1;
    }
    //入栈
    public void push(int value){
        if (isFull()){
            System.out.println("栈满");
            return;
        }
        top++;
        stack[top] = value;
    }
    //出栈
    public int pop() {
        if (isEmpty()){
            throw new RuntimeException("栈空");
        }
        int value = stack[top];
        top--;
        return value;
    }
    //遍历
    public void list(){
        if (isEmpty()){
            throw new RuntimeException("栈空");
        }
        for (int i = top; i >= 0; i--) {
            System.out.printf("stack[%d] = %d\n",i,stack[i]);
        }
    }

}

前缀表达式(波兰表达式)

  1. 前缀表达式又称波兰式,前缀表达式的运算符位于操作数之前
  2. (3+4)*5-6前缀表达式- * + 3 4 5 6

中缀表达式

中缀表达式求值就是正常做法,对计算机并不友好,计算时常常把中缀转成后缀

后缀表达式

  1. 后缀表达式称逆波兰表达式,与前缀表达式相似,运算符位于操作数之后
  2. (3+4)*5-6后缀表达式3 4 + 5 * 6 -
正常表达式逆波兰表达式
a + ba b +
a + ( b - c)a b c - +
a + (b - c) * da b c - d * +
a + d * (b - c)a d b c - * +
a = 1 + 3a 1 3 + =

从左到右扫描表达式,遇到数字时,数字压入堆栈,遇到运算符,弹出栈顶两个数,用运算符对他们做相应的计算,将结果入栈:重复上述过程直到表达式最右端,最后得出表达式的结果

不要忘了* / 优先于 + -

逆波兰计算器

  1. 输入一个逆波兰表达式(后缀表达式),使用栈(Stack),计算结果
  2. 支持小括号和多位数整数,这是简化计算器,只支持整数计算
package com.fu.datastruct.stack;


import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class PolandNotation {
    public static void main(String[] args) {
        String str = "3 4 + 5 * 6 -";
        System.out.println(calculate(getListString(str)));

    }
    //数据运算符放入ArrayList
    public static List<String> getListString(String str){
        String[] split = str.split(" ");
        List<String> list = new ArrayList<>();
        for (String s : split) {
            list.add(s);
        }
        return list;
    }
    //完成对逆波兰表达式计算
    public static int calculate(List<String> ls) {
        //创建一个栈,只需要一个栈即可
        Stack<String> stack = new Stack<>();
        for (String l : ls) {
            if (l.matches("\\d+")){
                stack.push(l);
            }
            else {
                int num2 = Integer.parseInt(stack.pop());
                int num1 = Integer.parseInt(stack.pop());
                int res = 0;
                if (l.equals("+")){
                    res = num1 + num2;
                }
                else if (l.equals("-")){
                    res = num1 - num2;
                }
                else if (l.equals("*")){
                    res = num1 * num2;
                }
                else if (l.equals("/")){
                    res = num1 / num2;
                }
                else {
                    throw new RuntimeException("输入错误!!!");
                }
                stack.push(res + "");
            }
        }
        return Integer.parseInt(stack.pop());
    }
}

中缀转后缀

后缀不好写出来,中缀好写

具体步骤:

  1. 初始化两个栈,运算符栈s1和 储存中间结果的栈s2
  2. 从左到右扫描中缀表达式
  3. 遇到操作数,将其压入s2
  4. 遇到运算符,比较与s1栈顶运算符优先级
  • 如果s1为空,或栈顶运算符为左括号,直接将此运算符入栈
  • 否则,若优先级比栈顶运算符高,将运算符压入s1
  • 否则,s1栈顶运算符弹出并压入s2中,再次转到(4-1)与s1中新的栈顶运算符比较
  1. 遇到括号时:
  • 如果是左括号,直接压入s1
  • 右括号,一次弹出s1栈顶运算符,压入s2,直到遇到左括号,此时这一对括号丢弃
  1. 重复步骤2-5,直到表达式最右边
  2. 将s1中剩余运算符一次弹出并压入s2
  3. 一次弹出s2中元素并输出,结果逆序即为中缀表达式对应表达式
package com.fu.datastruct.stack;


import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

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

        String e = "1+((2+3)*4)-5";//[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5]

        List<String> infixExpressionList = toInfixExpressionList(e);
        System.out.println("中缀表达式: "+infixExpressionList);
        List<String> suffixExpressionList = parseSuffixExpressionList(infixExpressionList);
        System.out.println("后缀表达式: "+suffixExpressionList);
        System.out.println(calculate(suffixExpressionList));


    }
    public static List<String> parseSuffixExpressionList(List<String> ls){
        Stack<String> s1 = new Stack<>();
        List<String> s2 = new ArrayList<>();

        for (String item : ls) {
            if (item.matches("\\d+")){
                s2.add(item);
            }else if (item.equals("(")){
                s1.push(item);
            }else if (item.equals(")")){
                while (!s1.peek().equals("(")){
                    s2.add(s1.pop());
                }
                s1.pop();
            }else {
                while (s1.size() != 0 && Operation.getValue(s1.peek()) >= Operation.getValue(item)){
                    s2.add(s1.pop());
            }
            s1.push(item);}
        }
        while (s1.size()!=0)s2.add(s1.pop());
        return s2;
    }

    public static List<String> toInfixExpressionList(String s){
        List<String> ls = new ArrayList<>();
        int i = 0;
        String s1;
        char c ;
        do {
            if ((c = s.charAt(i))<48||(c = s.charAt(i))>57){
                ls.add("" + c);
                i++;
            }
            else {
                s1 = "";
                while (i < s.length() && (c=s.charAt(i))>=48 && (c=s.charAt(i))<=57){
                    s1 += c;
                    i++;
                }
                ls.add(s1);
            }
        } while(i < s.length());
        return ls;
    }


    //完成对逆波兰表达式计算
    public static int calculate(List<String> ls) {
        //创建一个栈,只需要一个栈即可
        Stack<String> stack = new Stack<>();
        for (String l : ls) {
            if (l.matches("\\d+")){
                stack.push(l);
            }
            else {
                int num2 = Integer.parseInt(stack.pop());
                int num1 = Integer.parseInt(stack.pop());
                int res = 0;
                if (l.equals("+")){
                    res = num1 + num2;
                }
                else if (l.equals("-")){
                    res = num1 - num2;
                }
                else if (l.equals("*")){
                    res = num1 * num2;
                }
                else if (l.equals("/")){
                    res = num1 / num2;
                }
                else {
                    throw new RuntimeException("输入错误!!!");
                }
                stack.push(res + "");
            }
        }
        return Integer.parseInt(stack.pop());
    }
}
class Operation{
    private static int ADD = 1;
    private static int SUB = 1;
    private static int MUL = 2;
    private static int DIV = 2;
    public static int getValue(String operation){
        int result = 0;
        switch (operation){
            case "+":
                result = ADD;
                break;
            case "-":
                result = SUB;
                break;
            case "*":
                result = MUL;
                break;
            case "/":
                result = DIV;
                break;
            default:
                System.out.println("不存在该运算符!!!");
                break;
        }
        return result;
    }
}

递归

递归概念

递归就是方法自己调用自己,每次调用时传入不同的变量,递归有助于编程者解决复杂的问题,可以让代码更加简洁

递归遵守规则

  1. 执行一个方法,创建一个新的受保护的独立空间(栈空间)
  2. 方法局部变量是独立的,不会相互影响
  3. 方法中使用引用类型变量,就会共享该引用类型
  4. 递归必须向退出递归条件逼近,否则会无限递归,死鬼了
  5. 当一个方法执行完毕后,或者return返回,遵守谁调用就将结果返回给谁,当方法执行完毕或者返回时,该方法就执行完毕了
package com.fu.recursion;

public class MiGong {
    public static void main(String[] args) {
        int[][] map = new int[8][7];
        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;
        }//左右为1
        map[3][1] = 1;
        map[3][2] = 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();
        }
        setWay(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();
        }


    }

    /**
     *
     * @param map 表示地图
     * @param i 从哪个位置开始找
     * @param j
     * @return 如果找到通路,就返回true,否则返回false
     * 小球到达map[6][5]就说明通路找到了
     * map[i][j]=0表示该点没有走过,1表示墙,2表示可以走,3表示该位置已经走过但是走不通
     * 走迷宫时要确定一个策略,下 -> 右 -> 上 -> 左,如果该点走不通,再回溯
     *
     *
     */
    public static boolean setWay(int[][]map,int i,int j){
        if(map[6][5] == 2){
            return true;
        }else {
            if (map[i][j] == 0){
                map[i][j] = 2;
                if (setWay(map,i+1,j)){
                    return true;
                }else if (setWay(map,i,j+1)){
                    return true;
                }else if (setWay(map,i-1,j)){
                    return true;
                }else if (setWay(map,i,j-1)){
                    return true;
                }else {
                    map[i][j] = 3;
                    return false;
                }
            }
            else {//map[i][j] != 0 //1 2 3
                return false;
            }
        }
    }
}

排序

内部排序

指将需要处理的所有数据都加载到内部存储器(内存)中进行排序

外部排序

数据量大,无法全部加载到内存中,需要借助外部存储进行排序

常见排序算法

直接插入排序

希尔排序

简单选择排序

堆排序

冒泡排序

快速排序

归并排序

基数排序

度量一个程序(算法)执行时间的两种方法

  1. 事后执行方法

可行

但对设计算法运行性能进行评测,需要实际运行该程序

所得时间统计量依赖计算机硬件、软件等环境因素

这种方式要在同一台计算机相同状态运行,才能比较哪个算法速度更快

  1. 事前估算方法

通过分析某个方法时间复杂度来判断哪个算法更优

算法时间复杂度

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

时间频度

一个算法花费时间与语句执行次数成正比,哪个算法语句执行次数多,花费时间多,一个算法语句执行次数称为语句频度或时间频度

用常数1代替运行时间中所有加法常数

修改后运行次数函数中,只保留最高阶项

去除最高阶项系数

常见时间复杂度

  1. 常数阶O(1)
  2. 对数阶O(log 2 n)
  3. 线性阶O(n)
  4. 线性对数阶O(nlog 2 n)
  5. 平方阶 O(n^2)
  6. 立方阶O(n^3)
  7. k次方阶O(n^k)
  8. 指数阶O(2^n)

常见时间复杂度从小到大排序:O(1)<O(nlog 2 n)<O(n^2)

<O(n3)<O(nk)<O(2^n),随着问题规模n的不断增大,上述时间复杂度增大,算法执行效率变低

我们应该避免使用指数阶算法

  1. 平均时间复杂度指所有可能的输入实例均以等概率出现情况下,该算法运行时间

  2. 最坏情况时间复杂度称最坏时间复杂度,一般讨论时间复杂度均是最坏复杂度

    理由:最坏情况下时间复杂度是算法在任何输入实例上运行时间界限,就保证了算法运行时间不会比最坏情况更长

算法空间复杂度

  1. 类似时间复杂度,算法空间复杂度定义为该算法所耗费存储空间是问题规模n的函数
  2. 空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度,有的算法需要占用临时工作单元数与解决问题规模n有关,随着n的增大而增大,n较大,占用较多存储单元
  3. 做算法分析,主要讨论时间复杂度

冒泡排序

排序过程元素不断接近自己位置,一趟比较没有进行交换,说明序列有序,要在排序中设置flag判断元素是否进行过交换,减少不必要的比较

规则

  1. 进行数组大小-1次大的循环
  2. 每一趟排序次数逐渐减小
  3. 优化:如果我们发现在某趟排序中,没有进行一次交换,可以提前结束冒泡排序
package com.fu.sort;

import java.util.Arrays;

public class BubbleSort {
    public static void main(String[] args) {
        int a[] = {9,2,10,7,5,6,4,8,1,3};
        int temp;
        boolean flag = false;
        for (int i = 0; i < a.length-1; i++) {
            for (int j = 0; j < a.length-1-i; j++) {
                if (a[j]>a[j+1]){
                    flag = true;
                    temp = a[j];
                    a[j] = a[j+1];
                    a[j+1] = temp;
                }
            }
            if (!flag)break;
            else flag = false;
            System.out.println(Arrays.toString(a));
        }
    }
}

选择排序

原始的数组:101,34,119,1
第一轮排序:
1,34,119,101
第二轮排序:
1,34,119,101
第三轮排序:
1,34,101,119

说明:
1.选择排序一共有数组大小-1轮排序
2.每1轮排序,又是一个循环,循环的规则(代码)
2.1先假定当前这个数是最小数
2.2 然后和后面的每个数进行比较,如果发现有比当前数更小的数,
就重新确定最小数,并得到下标
2.3 当遍历到数组的最后时,就得到本轮最小数和下标
2.4 交换[代码]

package com.fu.sort;

import java.util.Arrays;

public class SelectSort {
    public static void main(String[] args) {
        int a[] = {101,34,119,1};
         System.out.println(Arrays.toString(a));
        selectSort(a);
        System.out.println(Arrays.toString(a));
    }
    public static void selectSort(int[] arr){
        for (int i = 0; i < arr.length - 1; i++) {
            int minIndex = i;
            int min = arr[i];
            for (int j = i + 1; j < arr.length; j++) {
                 if (min > arr[j]){
                    min = arr[j];
                    minIndex = j;
                }
            }
            if (minIndex != i){
                arr[minIndex] = arr[i];
                arr[i] = min;
            }
        }
    }
}

插入排序

插入式排序属于内部排序法,是对于欲排序的元素以插入的方式找寻该元素的
适当位置,以达到排序的目的。

思想:

插入排序(Insertion Sorting)的基本思想是:把n个待排序的元素看成为
一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中
包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,把它
的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的
适当位置,使之成为新的有序表。

package com.fu.sort;

import java.util.Arrays;

public class InsertSort {
    public static void main(String[] args) {
        int []arr = {101,29,232,1};
        insertSort(arr);
        System.out.println(Arrays.toString(arr));

    }
    public static void insertSort(int []arr){
        for (int i = 0; i < arr.length-1; i++) {
            int insertVal = arr[i+1];
            int insertIndex = i;
            while (insertIndex >= 0 && insertVal < arr[insertIndex]){
                arr[insertIndex + 1] = arr[insertIndex];
                insertIndex--;
            }
            arr[insertIndex + 1] = insertVal;
        }
    }
}

希尔排序

希尔排序法介绍

希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法。希尔排序也是
一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为
缩小增量排序。

希尔排序法基本思想

希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;
随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰
被分成一组,算法便终止

package com.fu.sort;

import java.util.Arrays;

/**
 * 希尔排序移动法(优化)
 */
public class ShellSort {
    public static void main(String[] args) {
        int []arr = {8,9,1,7,2,3,5,4,6,0};
        shellSort(arr);

    }
    public static void shellSort(int []arr){

        for (int gap = arr.length/2; gap > 0; gap/=2) {
            for (int i = gap; i < arr.length; i++) {
                int j = i;
                int temp = arr[j];
                if (arr[j] < arr[j-gap]){
                    while(j - gap >= 0 && temp < arr[j - gap]){
                        arr[j] = arr[j-gap];
                        j -= gap;
                    }
                    arr[j] = temp;
                }
            }
            System.out.println(Arrays.toString(arr));
        }
        }

    }


快速排序

快速排序(Quicksort)是对冒泡排序的一种改进。基本思想是:通过一趟排序
将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分
的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个
排序过程可以递归进行,以此达到整个数据变成有序序列
package com.fu.sort;

import java.util.Arrays;

public class QuickSort {
    public static void main(String[] args) {
        int []arr = {-9,78,0,23,-567,70};
        quickSort(arr,0,arr.length-1);
        System.out.println(Arrays.toString(arr));

    }

    /**
     *
     * @param arr  传入数组
     * @param left 左下标
     * @param right 右下标
     */
    public static void quickSort(int []arr,int left,int right){
        int l = left;
        int r = right;
        int pivot = arr[(left + right)/2];
        int temp = 0;
        //循环让比pivot值小的放左边,大的放右边
        while (l < r){
            //pivot左边找到大于等于pivot的值
            while (arr[l]<pivot){
                l += 1;
            }
            //pivot右边找到小于等于pivot的值
            while (arr[r]>pivot){
                r -= 1;
            }
            if (l >= r){
                break;
            }
            //交换
            temp = arr[l];
            arr[l] = arr[r];
            arr[r] = temp;

            //交换完,arr[l] == pivot值,相等--,前移
            if (arr[l] == pivot){
                r -= 1;
            }
            //交换完,arr[r] == pivot值,相等++,后移
            if (arr[r] == pivot){
                l += 1;
            }

        }
        if (l == r){
            l += 1;
            r -= 1;
        }
        if (left < r){
            quickSort(arr,left,r);
        }
        if (right > l){
            quickSort(arr,l,right);
        }



    }
}

归并排序

归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典
的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问
题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在
起,即分而治之)。
package com.fu.sort;

import java.util.Arrays;

public class MergerSort {
    public static void main(String[] args) {
        int arr[] = {8,4,5,7,1,3,6,2};
        int temp[] = new int[arr.length];
        mergeSort(arr,0 ,arr.length-1,temp);
        System.out.println(Arrays.toString(arr));
    }

    public static void mergeSort(int []arr,int left,int right,int[]temp ){
        if (left < right){
            int mid = (left + right)/2;
            mergeSort(arr,left,mid,temp);
            mergeSort(arr,mid+1,right,temp);
            merge(arr,left,mid,right,temp);
        }
    }
    /**
     *
     * @param arr  排序的原始数组
     * @param left 左边有序序列初始序列
     * @param mid  中间索引
     * @param right 右边索引
     * @param temp 做中转的数组
     */
    public static void merge(int []arr ,int left ,int mid ,int right ,int []temp){
        int i = left;
        int j = mid + 1;
        int t = 0;
        while (i <= mid && j<=right){


        if (arr[i]<=arr[j]){
            temp[t] = arr[i];
            t+=1;
            i+=1;
        }else {
            temp[t] = arr[j];
            t+=1;
            j+=1;
        }
    }
        while(i <= mid){
            temp[t] = arr[i];
            t+=1;
            i+=1;
        }
        while(j <= right){
            temp[t] = arr[j];
            t+=1;
            j+=1;
        }
        //拷贝temp数组
        t = 0;
        int templeft = left;
        while(templeft <= right){
            arr[templeft] = temp[t];
            t+=1;
            templeft+=1;
        }

}
}

基数排序(桶排序)

介绍
  1. 基数排序属于分配式排序,又称为桶子法或bin sort ,通过键值的各个位的值,将要排序的元素分配到桶中,达到排序的作用
  2. 基数排序法是属于稳定性排序,基数排序法是效率高的稳定性排序法
  3. 基数排序是桶排序的扩展
  4. 家属排序将整数按位数切割成不同的数字,按照每个位数分别比较
基本思想
  1. 将所有比较数值统一为同样的数位长度,数位短的数前面补零,然后,从最低为开始,依次进行依次排序,从最低位排序一直到最高位排序后,数列变成一个有序序列
package com.fu.sort;

import java.util.Arrays;

public class RadixSort {
    public static void main(String[] args) {
        int arr[] = {53, 3, 542, 748, 14, 214};
        radixSort(arr);

    }

    public static void radixSort(int[] arr) {
        int max = arr[0];
        for (int i = 0; i < arr.length; i++) {
            if (arr[i]>max){
                max = arr[i];
            }
        }
        int maxLength = (max + "").length();


        int[][] bucket = new int[10][arr.length];
        int[] bucketElementCounts = new int[10];

        for (int i = 0,n=1; i < maxLength; i++,n*=10) {
            for (int j = 0; j < arr.length; j++) {
                int digitOfElement = arr[j] /n % 10;
                bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
                bucketElementCounts[digitOfElement]++;
            }
            int index = 0;
            for (int k = 0; k < bucketElementCounts.length; k++) {
                if (bucketElementCounts[k] != 0){
                    for (int l = 0; l < bucketElementCounts[k]; l++) {
                        arr[index++] = bucket[k][l];
                    }
                }
                bucketElementCounts[k] = 0;
            }
            System.out.println(Arrays.toString(arr));
        }
        
    }
}

常见排序算法比较和总结

  1. 稳定
  2. 不稳定
  3. 内排序
  4. 外排序
  5. 时间复杂度
  6. 空间复杂度
  7. 数据规模
  8. 桶数量
  9. 不占额外内存
  10. 占用额外内存

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ytF671hd-1643456651036)(C:\Users\11021\AppData\Roaming\Typora\typora-user-images\1643267909953.png)]

查找

java中常用查找:

  1. 顺序(线性)查找
  2. 二分查找/折半查找
  3. 插值查找
  4. 斐波那契查找(黄金分割点查找)

顺序(线性)查找

最简单的查找,思路就是从头到尾查找

package com.fu.search;

public class SeqSearch {
    public static void main(String[] args) {
        int arr[] = {1,9,11,-1,34,89};
        int index = seqSearch(arr,11);
        if (index == -1){
            System.out.println("没有找到");
        }else {
            System.out.println("找到,下标:"+ index);
        }
    }

    /**
     *
     * @param arr 
     * @param value
     * @return
     */
    public static int seqSearch(int []arr,int value){
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] == value){
                return i;
            }
        }
        return -1;
    }
}

二分查找

思路:

  1. 1. 首先确定数组中间下标
    2. 然后让需要查找的数比较
    3. 要查找的数在mid左边,递归的向右查找
    4. 要查找的数在mid右边,递归的向左查找
    5. 找到即返回,结束递归
    6. 递归完整个数组,仍然没有找到,也要结束递归
    
package com.fu.search;

//二分查找数组必须有序
public class BinarySearch {
    public static void main(String[] args) {
        int arr[] = {1, 8, 10, 89, 1000, 1234};
        System.out.println(binarySearch(arr, 0, arr.length - 1, 88));
    }
    /**
     *
     * @param arr 数组
     * @param left 左边索引
     * @param right 右边索引
     * @param findVal 要查找的值
     * @return 找到返回下标,找不到返回-1
     */
    public static int binarySearch(int []arr,int left,int right,int findVal){
        int mid = (left + right)/2;
        int midVal = arr[mid];

        if (left> right){return  -1;}
        if (findVal > midVal){
            return binarySearch(arr,mid + 1,right,findVal);
        }
        else if (findVal < midVal){
            return binarySearch(arr,left,mid-1,findVal);
        }else {
            return mid;
        }

    }
}

插值查找

  1. 插值查找类似二分查找,不同的是插值查找每次从自适应mid开始查找
  2. int midIndex = low + (high - low)*(key - arr[low])/(arr[high] - arr[low])\
package com.fu.search;

import java.util.Arrays;

public class InsertSearch {
    public static void main(String[] args) {
        int []arr = new int[100];
        for (int i = 0; i < 100; i++) {
            arr[i] = i+1;
        }
        System.out.println(insertValueSearch(arr, 0, arr.length-1, 9));
    }

    /**
     * 插值查找要求数组有序
     * @param arr 数组
     * @param left 左边索引
     * @param right 右边索引
     * @param findVal 查找值
     * @return
     */
    public static int insertValueSearch(int []arr,int left,int right,int findVal){
        if(left > right || findVal < arr[0] || findVal > arr[arr.length-1]){
            return -1;
        }
        int mid = left + (right - left) * (findVal - arr[left])/(arr[right] - arr[left]);
        int midVal = arr[mid];
        if (findVal > midVal){//说明应该向右边递归
            return insertValueSearch(arr,mid+1,right,findVal);
        } else if (findVal<midVal){//说明应该向左边递归
            return insertValueSearch(arr,left,mid-1,findVal);
        } else {
            return mid;
        }
    }
}

斐波拉契查找 0.168

黄金分割点值把一条线段分割两部分,一部分与全长之比等于另一部分与这部分之比

取其前三位数字近似值0.618,由于按此比例设计的造型十分美丽,因此成为黄金分割,称为中外比,一个神奇的数字,带来意想不到效果

斐波拉契数列{1,1,2,3,5,8,13,21,34,55}发现斐波那契数列两个相邻数比例无限接近黄金分割值0.168

package com.fu.search;

import java.util.Arrays;

public class FibonacciSearch {
    public static int maxSize = 20;
    public static void main(String[] args) {
        int []arr = {1,8,10,89,1000,1234};
        System.out.println(fibSearch(arr,1));
    }
    //得到一个斐波拉契数列
    public static int[] fib(){
        int []f = new int [maxSize];
        f[0] = 1;
        f[1] = 1;
        for (int i = 2; i < maxSize; i++) {
            f[i] = f[i-1] + f[i-2];
        }
        return f;
    }

    /**
     *
     * @param a 数组
     * @param key 需要的关键码
     * @return 返回对应下标/-1
     */
    public static int fibSearch(int [] a,int key){
        int low = 0;
        int high = a.length - 1;
        int k = 0;
        int mid = 0;
        int f[] = fib();//获取斐波拉契数列
        //获取斐波拉契数列下标
        while (high > f[k]-1){
            k++;
        }
        int []temp = Arrays.copyOf(a,f[k]);
        for (int i = high+1; i < temp.length; i++) {
            temp[i] = a[high];
        }
        while(low <= high){
            mid = low + f[k-1] -1;
            if (key < temp[mid]){
                high = mid - 1;
                k--;
            }
            else if (key > temp[mid]){
                low = mid + 1;
                k -= 2;
            }else {
                if (mid <= high){
                    return mid;
                }else {
                    return high;
                }
            }

        }return -1;





    }

}

哈希表

散列表又叫哈希表,是根据关键码值直接进行访问数据结构,也就是说,通过关键码映射到表中的一个位置来访问记录,加快查找速度,这个映射函数叫散列函数,存放记录数组是散列表

哈希表结构:

数组 + 链表

数组 + 二叉树

基本介绍

散列表根据关键码值直接进行访问的数据结构,就是说,通过把关键码值映射到表中的一个位置来访问记录,加快查找的速度,这个映射函数叫散列函数,存放记录的数组叫散列表

package com.fu.hashtab;
import java.util.Scanner;

public class HashTabDemo {
    public static void main(String[] args) {
        HashTab hashTab = new HashTab(10);
        String key = "";
        Scanner scanner = new Scanner(System.in);
        while(true) {
            System.out.println("add 添加雇员");
            System.out.println("list 显示雇员");
            System.out.println("find 查找雇员");
            System.out.println("exit 退出系统");
            key = scanner.next();
            switch (key){
                case "add":
                    System.out.println("输入id");
                    int id = scanner.nextInt();
                    System.out.println("输入名字");
                    String name = scanner.next();
                    Emp emp = new Emp(id, name);
                    hashTab.add(emp);
                    break;
                case "list":
                    hashTab.list();
                    break;
                case "find":
                    System.out.println("输入要查找的id");
                    id = scanner.nextInt();
                    hashTab.findEmpById(id);
                    break;
                case "exit":
                    scanner.close();
                    System.exit(0);
                default:
                    break;
            }
        }
    }
}
class Emp{
    public int id;
    public String name;
    public Emp next;//默认为空

    public Emp(int id, String name) {
        this.id = id;
        this.name = name;
    }
}
class HashTab{
    private EmpLinkedList[] empLinkedListArray;
    private int size;
    public HashTab(int size) {
        this.size = size;
        empLinkedListArray = new EmpLinkedList[size];
        //初始化每一条链表
        for (int i = 0; i < size; i++) {
            empLinkedListArray[i] = new EmpLinkedList();
        }
    }
    public void add(Emp emp){
        int empLinkedListNO = hashFun(emp.id);
        empLinkedListArray[empLinkedListNO].add(emp);
    }
    public int hashFun(int id){
        return id % size;
    }
    public void list(){//遍历hash表
        for (int i = 0; i < size; i++) {
            empLinkedListArray[i].list(i);
        }
    }
    public void findEmpById(int id){
        int empLinkedListNO = hashFun(id);
        Emp emp = empLinkedListArray[empLinkedListNO].findEmpById(id);
        if (emp != null){
            System.out.printf("在%d条链表中找到雇员id = %d\n",(empLinkedListNO + 1),id);
        }else{
            System.out.println("在哈希表中没有找到该雇员");
        }
    }

}
class EmpLinkedList{
    private Emp head;
    //添加
    public void add(Emp emp){
        if (head == null){
            head = emp;
            return;
        }
        Emp curEmp = head;
        while (true){
            if (curEmp.next == null){
                break;
            }
            curEmp = curEmp.next;
        }
        curEmp.next = emp;
    }
    //遍历
    public void list(int no){
        if (head == null){
            System.out.println("第"+(no+1)+"当前链表为空");
            return;
        }
        System.out.println("第"+(no+1)+"条链表信息为:");
        Emp curEmp = head;
        while(true){
            System.out.printf("=> id = %d name = %s\t",curEmp.id,curEmp.name);
            if (curEmp.next == null){
                break;
            }
            curEmp=curEmp.next;
        }
        System.out.println();
    }
    public Emp findEmpById(int id){
        if (head == null){
            System.out.println("链表为空");
        }
        Emp curEmp = head ;
        while (true){
            if (curEmp.id == id){
                break;
            }
            if (curEmp.next == null){
                curEmp = null;
            }
            curEmp = curEmp.next;
        }
        return curEmp;

    }

}

树 vs 数组 vs 链表

数组的插入删除效率低

链表的查找效率低

数保证了各项的速度

二叉树

  1. 树有很多种,每个节点最多有两个子节点的一种形式叫二叉树
  2. 二叉树子节点有左节点和右节点
  3. 二叉树所有叶子节点在最后一层,结点总数2^n-1,即满二叉树
  4. 如果二叉树所有叶子节点在最后一层或倒数第二层,最后一层叶子节点向左边连续,倒数第二层叶子节点向右边连续,称为满二叉树
树的遍历

创建一颗二叉树

前序遍历:

先输出父节点,再遍历左子树和右子树

如果左子节点不为空,则递归继续前序遍历

如果右子节点不为空,则递归继续前序遍历

中序遍历:

先遍历左子树,再输出父节点,再遍历右子树

如果当前节点的左子节点不为空,递归中序遍历

输出当前节点

如果当前节点的右子节点不为空,递归中序遍历

后序遍历:

先遍历左子树和右子树,再输出父节点

看输出父节点的顺序,确定前序和后序

如果当前节点的左子节点不为空,递归后序遍历

如果当前节点的右子节点不为空,递归中序遍历

输出当前节点

树的常用术语:
  1. 节点
  2. 根节点
  3. 父节点
  4. 子节点
  5. 叶子节点(没有子节点的节点)
  6. 节点的权(节点值)
  7. 路径(从root节点找到该节点的路线)
  8. 子树
  9. 树的高度(最大层数)
  10. 森林(多颗子树构成森林)
package com.fu.tree;

public class BinaryTreeDemo {
    public static void main(String[] args) {
        //创建一棵二叉树
        BinaryTree binaryTree = new BinaryTree();
        HeroNode root = new HeroNode(1, "宋江");
        HeroNode node2 = new HeroNode(2, "吴用");
        HeroNode node3 = new HeroNode(3, "卢俊义");
        HeroNode node4 = new HeroNode(4, "林冲");

        //手动创建二叉树,后边用递归创建
        root.setLeft(node2);
        root.setRight(node3);
        node3.setRight(node4);

        System.out.println("前序遍历");
        binaryTree.setRoot(root);
        binaryTree.preOrder();
        System.out.println("后序遍历");
        binaryTree.infixOrder();
        System.out.println("中序遍历");
        binaryTree.postOrder();
        



    }
}
class BinaryTree{
    private HeroNode root;

    public void setRoot(HeroNode root) {
        this.root = root;
    }
    //前序遍历
    public void preOrder(){
        if (this.root != null){
            this.root.preOrder();
        }
        else {
            System.out.println("二叉树为空,无法遍历");
        }
    }

    //中序遍历
    public void infixOrder(){
        if (this.root != null){
            this.root.infixOrder();
        }
        else {
            System.out.println("二叉树为空,无法遍历");
        }
    }
    //后序遍历
    public void postOrder(){
        if (this.root != null){
            this.root.postOrder();
        }else {
            System.out.println("二叉树为空,无法遍历");
        }
    }

}


class HeroNode{
    private int no;
    private String name;
    private HeroNode left;
    private HeroNode right;

    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public HeroNode getLeft() {
        return left;
    }

    public void setLeft(HeroNode left) {
        this.left = left;
    }

    public HeroNode getRight() {
        return right;
    }

    public void setRight(HeroNode right) {
        this.right = right;
    }

    @Override
    public String toString() {
        return "HeroNode[" +
                "no=" + no +
                ", name='" + name + '\'' +
                ']';
    }

    //前序遍历
     public void preOrder(){
         System.out.println(this);
         //递归向左子树前序遍历
         if (this.left != null){
             this.left.preOrder();
         }
         //递归向右子树前序遍历
         if (this.right != null){
             this.right.preOrder();
         }
     }

    //中序遍历
    public void infixOrder(){
        //递归向左子树中序遍历
        if (this.left != null){
            this.left.infixOrder();
        }
        //输出父节点
        System.out.println(this);
        //递归向右子树中序遍历
        if (this.right != null){
            this.right.infixOrder();
        }

    }
    //后序遍历
    public void postOrder(){
        if (this.left != null){
            this.left.postOrder();
        }
        if (this.right != null){
            this.right.postOrder();
        }
        System.out.println(this);
    }



}

二叉树查找

  1. 前序查找,中序查找,后序查找方法
  2. 分别使用三种查找方式,查找heroNO=5的节点
  3. 分析各种查找方法,分别比较多少次
前序查找思路:
  1. 判断当前节点no是否等于要查找的
  2. 相等则返回当前节点
  3. 不等,判断当前节点是否为空,不为空则递归前序查找
  4. 左递归前序查找,找到节点,返回,否则继续判断,当前节点右节点是否为空,不为空,继续向右递归前序查找
中序查找思路
  1. 判断当前节点左子节点是否为空,如果不为空,递归中序查找
  2. 如果找到,返回,没找到,与当前节点比较,是则返回当前节点,否则继续右递归中序查找
  3. 有递归中序查找,找到返回,否则null
后序查找思路
  1. 判断当前节点左子节点是否空,不为空,递归后续查找
  2. 找到返回,没找到,判断当前节点右子节点是否为空,不为空,有递归后续查找,找到返回
  3. 和当前节点进行,是返回,否则null
package com.fu.tree;

public class BinaryTreeDemo {
    public static void main(String[] args) {
        //创建一棵二叉树
        BinaryTree binaryTree = new BinaryTree();
        HeroNode root = new HeroNode(1, "宋江");
        HeroNode node2 = new HeroNode(2, "吴用");
        HeroNode node3 = new HeroNode(3, "卢俊义");
        HeroNode node4 = new HeroNode(4, "林冲");

        //手动创建二叉树,后边用递归创建
        root.setLeft(node2);
        root.setRight(node3);
        node3.setRight(node4);

        System.out.println("前序遍历");
        binaryTree.setRoot(root);
        binaryTree.preOrder();
        System.out.println("后序遍历");
        binaryTree.infixOrder();
        System.out.println("中序遍历");
        binaryTree.postOrder();

        HeroNode resNode = binaryTree.preOrderSearch(4);
        if (resNode != null){
            System.out.printf("找到了,信息no=%d name=%s",resNode.getNo(),resNode.getName());
        }else {
            System.out.printf("没有找到no = %d的英雄",5);
        }



    }
}
class BinaryTree{
    private HeroNode root;

    public void setRoot(HeroNode root) {
        this.root = root;
    }
    //前序遍历
    public void preOrder(){
        if (this.root != null){
            this.root.preOrder();
        }
        else {
            System.out.println("二叉树为空,无法遍历");
        }
    }

    //中序遍历
    public void infixOrder(){
        if (this.root != null){
            this.root.infixOrder();
        }
        else {
            System.out.println("二叉树为空,无法遍历");
        }
    }
    //后序遍历
    public void postOrder(){
        if (this.root != null){
            this.root.postOrder();
        }else {
            System.out.println("二叉树为空,无法遍历");
        }
    }
    //前序查找
    public HeroNode preOrderSearch(int no){
        if (root != null){
            return root.postOrderSearch(no);
        }else {
            return null;
        }
    }
    //中序查找
    public HeroNode infixOrderSearch(int no){
        if (root != null){
            return root.preOrderSearch(no);
        }else {
            return null;
        }
    }
    //后序遍历
    public HeroNode postOrderSearch(int no) {
        if (root != null) {
            return root.postOrderSearch(no);
        }else {
            return null;
        }


    }
}


class HeroNode{
    private int no;
    private String name;
    private HeroNode left;
    private HeroNode right;

    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public HeroNode getLeft() {
        return left;
    }

    public void setLeft(HeroNode left) {
        this.left = left;
    }

    public HeroNode getRight() {
        return right;
    }

    public void setRight(HeroNode right) {
        this.right = right;
    }

    @Override
    public String toString() {
        return "HeroNode[" +
                "no=" + no +
                ", name='" + name + '\'' +
                ']';
    }

    //前序遍历
     public void preOrder(){
         System.out.println(this);
         //递归向左子树前序遍历
         if (this.left != null){
             this.left.preOrder();
         }
         //递归向右子树前序遍历
         if (this.right != null){
             this.right.preOrder();
         }
     }

    //中序遍历
    public void infixOrder(){
        //递归向左子树中序遍历
        if (this.left != null){
            this.left.infixOrder();
        }
        //输出父节点
        System.out.println(this);
        //递归向右子树中序遍历
        if (this.right != null){
            this.right.infixOrder();
        }

    }
    //后序遍历
    public void postOrder(){
        if (this.left != null){
            this.left.postOrder();
        }
        if (this.right != null){
            this.right.postOrder();
        }
        System.out.println(this);
    }

    //前序查找

    /**
     *
     * @param no 查找no
     * @return 找到返回Node,没找到返回null
     */
    public HeroNode preOrderSearch(int no){
        if (this.no == no){
            return this;
        }
        HeroNode resNode = null;
        if (this.left != null){
          resNode =   this.left.preOrderSearch(no);
        }
        if(resNode != null){
         return resNode;
        }
        if (this.right != null){
                resNode = this.right.preOrderSearch(no);
        }
        return resNode;
    }
    //中序遍历
    public HeroNode infixOrderSearch(int no){
        HeroNode resNode = null;
        if (this.left != null){
            resNode = this.left.infixOrderSearch(no);
        }
        if (resNode != null){
            return resNode;
        }
        if (this.no == no){
            return this;
        }
        if (this.right != null){
            resNode = this.right.infixOrderSearch(no);
        }
        return resNode;
    }
    //后
    public HeroNode postOrderSearch(int no){
        HeroNode resNode = null;
        if (this.left != null){
            resNode = this.left.postOrderSearch(no);
        }
        if (resNode != null){
            return resNode;
        }
        if (this.right != null){
            resNode =this.right.postOrderSearch(no);
        }
        if (resNode != null){
            return resNode;
        }
        if (this.no == no){
            return this;
        }
        return resNode;
    }

}

二叉树删除节点

  1. 删除节点是叶子节点,删除该节点
  2. 删除节点是非叶子节点,删除该子树
思路:
  1. 如果树是空树root,只有一个root节点,等价将二叉树置空

  2. 、我们二叉树单向的,所以我们判断当前节点的子节点是否需要删除节点,不能去判断这个节点是不是需要删除节点

  3. 如果当前节点的左子节点不为空,并且左子节点就是要删除的节点,将this.left = null,并且返回(结束递归删除)

  4. 如果当前节点右子节点不为空,并且右子节点就是要删除的节点,this.right = null,并且返回(结束递归删除)

  5. 第二步和第三步没有删除节点,我们就需要向左子树进行递归删除

  6. 第四步没有删除节点,应当行啊右子树进行递归删除

    package com.fu.tree;
    
    public class BinaryTreeDemo {
        public static void main(String[] args) {
            //创建一棵二叉树
            BinaryTree binaryTree = new BinaryTree();
            HeroNode root = new HeroNode( 1, "宋江");
            HeroNode node2 = new HeroNode(2, "吴用");
            HeroNode node3 = new HeroNode(3, "卢俊义");
            HeroNode node4 = new HeroNode(4, "林冲");
    
            //手动创建二叉树,后边用递归创建
            root.setLeft(node2);
            root.setRight(node3);
            node3.setRight(node4);
    
            System.out.println("前序遍历");
            binaryTree.setRoot(root);
            binaryTree.preOrder();
            System.out.println("后序遍历");
            binaryTree.infixOrder();
            System.out.println("中序遍历");
            binaryTree.postOrder();
    
            HeroNode resNode = binaryTree.preOrderSearch(4);
            if (resNode != null){
                System.out.printf("找到了,信息no=%d name=%s",resNode.getNo(),resNode.getName());
            }else {
                System.out.printf("没有找到no = %d的英雄",5);
            }
            System.out.println("删除前,前序遍历~~~");
            binaryTree.preOrder();
            binaryTree.delNode(5);
            System.out.println("删除后,前序遍历~~~");
            binaryTree.preOrder();
    
        }
    }
    class BinaryTree{
        private HeroNode root;
    
        public void setRoot(HeroNode root) {
            this.root = root;
        }
        //删除节点
        public void delNode(int no){
            if (root != null){
                if (root.getNo() == no){
                    root = null;
                }else {
                    root.delNode(no);
                }
            }else {
                System.out.println("空数不能删除");
            }
        }
        //前序遍历
        public void preOrder(){
            if (this.root != null){
                this.root.preOrder();
            }
            else {
                System.out.println("二叉树为空,无法遍历");
            }
        }
    
        //中序遍历
        public void infixOrder(){
            if (this.root != null){
                this.root.infixOrder();
            }
            else {
                System.out.println("二叉树为空,无法遍历");
            }
        }
        //后序遍历
        public void postOrder(){
            if (this.root != null){
                this.root.postOrder();
            }else {
                System.out.println("二叉树为空,无法遍历");
            }
        }
        //前序查找
        public HeroNode preOrderSearch(int no){
            if (root != null){
                return root.postOrderSearch(no);
            }else {
                return null;
            }
        }
        //中序查找
        public HeroNode infixOrderSearch(int no){
            if (root != null){
                return root.preOrderSearch(no);
            }else {
                return null;
            }
        }
        //后序遍历
        public HeroNode postOrderSearch(int no) {
            if (root != null) {
                return root.postOrderSearch(no);
            }else {
                return null;
            }
    
    
        }
    }
    
    
    class HeroNode{
        private int no;
        private String name;
        private HeroNode left;
        private HeroNode right;
    
        public HeroNode(int no, String name) {
            this.no = no;
            this.name = name;
        }
    
        public int getNo() {
            return no;
        }
    
        public void setNo(int no) {
            this.no = no;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public HeroNode getLeft() {
            return left;
        }
    
        public void setLeft(HeroNode left) {
            this.left = left;
        }
    
        public HeroNode getRight() {
            return right;
        }
    
        public void setRight(HeroNode right) {
            this.right = right;
        }
    
        @Override
        public String toString() {
            return "HeroNode[" +
                    "no=" + no +
                    ", name='" + name + '\'' +
                    ']';
        }
        //递归删除节点
        public void delNode(int no){
            if (this.left != null && this.left.no == no){
                this.left = null;
                return;
            }
            if (this.right != null && this.right.no == no){
                this.right = null;
                return;
            }
            if (this.left != null){
                this.left.delNode(no);
            }
            if (this.right != null){
                this.right.delNode(no);
            }
    
        }
    
        //前序遍历
         public void preOrder(){
             System.out.println(this);
             //递归向左子树前序遍历
             if (this.left != null){
                 this.left.preOrder();
             }
             //递归向右子树前序遍历
             if (this.right != null){
                 this.right.preOrder();
             }
         }
    
        //中序遍历
        public void infixOrder(){
            //递归向左子树中序遍历
            if (this.left != null){
                this.left.infixOrder();
            }
            //输出父节点
            System.out.println(this);
            //递归向右子树中序遍历
            if (this.right != null){
                this.right.infixOrder();
            }
    
        }
        //后序遍历
        public void postOrder(){
            if (this.left != null){
                this.left.postOrder();
            }
            if (this.right != null){
                this.right.postOrder();
            }
            System.out.println(this);
        }
    
        //前序查找
    
        /**
         *
         * @param no 查找no
         * @return 找到返回Node,没找到返回null
         */
        public HeroNode preOrderSearch(int no){
            if (this.no == no){
                return this;
            }
            HeroNode resNode = null;
            if (this.left != null){
              resNode =   this.left.preOrderSearch(no);
            }
            if(resNode != null){
             return resNode;
            }
            if (this.right != null){
                    resNode = this.right.preOrderSearch(no);
            }
            return resNode;
        }
        //中序遍历
        public HeroNode infixOrderSearch(int no){
            HeroNode resNode = null;
            if (this.left != null){
                resNode = this.left.infixOrderSearch(no);
            }
            if (resNode != null){
                return resNode;
            }
            if (this.no == no){
                return this;
            }
            if (this.right != null){
                resNode = this.right.infixOrderSearch(no);
            }
            return resNode;
        }
        //后
        public HeroNode postOrderSearch(int no){
            HeroNode resNode = null;
            if (this.left != null){
                resNode = this.left.postOrderSearch(no);
            }
            if (resNode != null){
                return resNode;
            }
            if (this.right != null){
                resNode =this.right.postOrderSearch(no);
            }
            if (resNode != null){
                return resNode;
            }
            if (this.no == no){
                return this;
            }
            return resNode;
        }
    
    }
    
    

!= null){
this.left.delNode(no);
}
if (this.right != null){
this.right.delNode(no);
}

   }

   //前序遍历
    public void preOrder(){
        System.out.println(this);
        //递归向左子树前序遍历
        if (this.left != null){
            this.left.preOrder();
        }
        //递归向右子树前序遍历
        if (this.right != null){
            this.right.preOrder();
        }
    }

   //中序遍历
   public void infixOrder(){
       //递归向左子树中序遍历
       if (this.left != null){
           this.left.infixOrder();
       }
       //输出父节点
       System.out.println(this);
       //递归向右子树中序遍历
       if (this.right != null){
           this.right.infixOrder();
       }

   }
   //后序遍历
   public void postOrder(){
       if (this.left != null){
           this.left.postOrder();
       }
       if (this.right != null){
           this.right.postOrder();
       }
       System.out.println(this);
   }

   //前序查找

   /**
    *
    * @param no 查找no
    * @return 找到返回Node,没找到返回null
    */
   public HeroNode preOrderSearch(int no){
       if (this.no == no){
           return this;
       }
       HeroNode resNode = null;
       if (this.left != null){
         resNode =   this.left.preOrderSearch(no);
       }
       if(resNode != null){
        return resNode;
       }
       if (this.right != null){
               resNode = this.right.preOrderSearch(no);
       }
       return resNode;
   }
   //中序遍历
   public HeroNode infixOrderSearch(int no){
       HeroNode resNode = null;
       if (this.left != null){
           resNode = this.left.infixOrderSearch(no);
       }
       if (resNode != null){
           return resNode;
       }
       if (this.no == no){
           return this;
       }
       if (this.right != null){
           resNode = this.right.infixOrderSearch(no);
       }
       return resNode;
   }
   //后
   public HeroNode postOrderSearch(int no){
       HeroNode resNode = null;
       if (this.left != null){
           resNode = this.left.postOrderSearch(no);
       }
       if (resNode != null){
           return resNode;
       }
       if (this.right != null){
           resNode =this.right.postOrderSearch(no);
       }
       if (resNode != null){
           return resNode;
       }
       if (this.no == no){
           return this;
       }
       return resNode;
   }

}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值