数据结构笔记(一)—— 稀疏数组 、队列、链表、栈、递归 (包含java代码实现)

稀疏数组

image-20210413215057140

image-20210415165520882

package sparseArray;

import java.io.*;
import java.util.Arrays;

/**
 * @author lixiangxiang
 * @description 稀疏数组
 * @date 2021/4/15 16:48
 */
public class SpareArray {

    public static void main(String[] args) throws IOException {
        //创建一个11*11的二维数组
        int [][] chessArray = new int[11][11];
        chessArray[1][2] = 1;
        chessArray[2][3] = 2;
        chessArray[3][4] = 3;
        //输出原始二维数组
//        for (int[] row : chessArray) {
//            for (int data : row) {
//                System.out.print(data+"\t");
//            }
//            System.out.println();
//        }
        writeSpareArray(chessArray);
        int[][] chessArray2 = reduceSpareArray();
        assert chessArray2 != null;
        for (int[] row : chessArray2) {
            for (int data : row) {
                System.out.print(data+"\t");
            }
            System.out.println();
        }
    }

    /**
     * description: 将二维数组转为稀疏数组并写入文件中
     *
     * @author: lixiangxiang
     * @param source
     * @return void
     * @date 2021/8/9 9:50
     */
    private static void writeSpareArray (int[][] source) {
        try {
            int sum = 0;
            for (int[] row : source) {
                for (int data : row) {
                    if (data != 0) {
                        sum ++;
                    }
                }
            }
            int[][] spareArray = new int[sum+1][3];
            spareArray[0][0] = source.length;
            spareArray[0][1] = source[0].length;
            int count = 0;
            for (int i = 0; i < source.length; i++) {
                for (int j = 0; j < source[i].length; j++) {
                    if (source[i][j] != 0) {
                        count++;
                        spareArray[count][0] = i;
                        spareArray[count][1] = j;
                        spareArray[count][2] = source[i][j];
                    }
                }
            }
            //写入文件中
            String filePath = "src/text/spareArray.txt";
            File file = new File(filePath);

            if(file.exists()) {
                file.delete();
            }
            if(file.createNewFile()) {
                FileWriter out = new FileWriter(file);
                for (int[] row : spareArray) {
                    for (int data : row) {
                        out.write(data+"\t");
                    }
                    out.write("\r\n");
                }
                out.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * description: 从文件读取稀疏数组并转回二维数组
     *
     * @author: lixiangxiang 
     * @return int[][]
     * @date 2021/8/9 9:53 
     */
    private static int[][]  reduceSpareArray() {
        try {
            String filePath = "src/text/spareArray.txt";
            File file = new File(filePath);
            BufferedReader fileReader = new BufferedReader(new FileReader(file));
            String line = fileReader.readLine();
            //将数据转换为int类型
            String[] split = line.split("\t");
            Integer[] lines = Arrays.stream(split).map(Integer::parseInt).toArray(Integer[]::new);
            int[][] chessArray = new int[lines[0]][lines[1]];
            while((line = fileReader.readLine())!=null) {
                split = line.split("\t");
                lines = Arrays.stream(split).map(Integer::parseInt).toArray(Integer[]::new);
                chessArray[lines[0]][lines[1]] = lines[2];
            }
            return chessArray;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

队列

队列介绍

  • 队列是一个有序列表,可以用数组或是链表来实现

  • 遵循先入先出原。即:先存入队列的数据,要先取出。后存入的,后取出

  • 示意图:

    image-20210416163522470

数组模拟队列

  • 队列本身是有序列表,若使用数组的结构来储存队列的数据,则队列数组的声明如下图,其中maxSize是该队列的最大容量

  • 队列的输出、输入是分别从前后端处理的,因此用练个变量front和rear分别记录队列前后端的下标,front会随着数据输出而改变,而rear是随着数据输入而改变

  • 当我们将数据存入队列是称为“addQueue”,addQueue的处理需要有两个步骤:

    • 将尾指针往后移:rear+1,当front == rear 表示队列中没有数据

    • 若尾指针rear小于队列的最大下标 maxSize-1,则将数据存入rear所指的数组元素中,否则无法存入数据。rear == maxSize - 1[队列满]

问题

使用一次之后就不能用了。

数组模拟环形队列

  1. front含义调整:front指向队列的第一个元素,front的初始值为0。
  2. rear 含义调整:rear指向队列的最后一个元素的后一个位置,因为希望做出一个约定,rear的初始值是0。
  3. 当队列满时,条件是 (rear + 1)%maxsize = front 【满】
  4. 当队列为空的条件,rear = front空
  5. 队列中有效的数据的个数==(rear + maxSize -front) % maxSize== rear = 1 front = 0

代码实现

package queue;

import sun.security.util.ArrayUtil;

import java.util.Arrays;

/**
 * @author lixiangxiang
 * @description /
 * @date 2021/4/16 16:56
 */
public class ArrayQueue {
    //数组的最大容量
    private int maxSize;
    /**
     * 队列头
     */
    private int front ;
    /**
     * 队列尾
     */
    private int rear;
    /**
     * 改数组用于存入数据。模拟队列
     */
    private int[] arr;


    public ArrayQueue(int arrMaxSize) {
        maxSize = arrMaxSize;
        arr = new int[maxSize];
        front = -1; //指向队列头
        rear = -1; //指向队列尾

    }

    //判断队列是否满
    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("队列空");
        }
        return arr[++front];
    }
    //显示队列的所有数据
    public void show() {
        if (isEmpty()) {
            System.out.println("队列为空");
            return;
        }
        Arrays.toString(arr);
    }
    //显示队列的头数据,注意不是取出数据
    public int headQueue() {
        if (isEmpty()) {
            System.out.println("队列空,没有数据");
        }
        return arr[front + 1];
    }
}

package queue;


/**
 * @author lixiangxiang
 * @description 环形队列
 * @date 2021/4/18 9:14
 */
public class CircleQueue {
    //指向队列第一个数
    private int front;
    //队列尾部
    private int rear;
    //队列的大小
    private int maxSize;
    //数组大小
    private int arrSize;
    //模拟队列的数组
    private int[] queueArr;

    /**
     * 初始化
     */
    public CircleQueue(int size) {
        front = 0;
        rear = 0;
        maxSize = size ;
        arrSize = size + 1;
        queueArr = new int[arrSize];
    }

    /**
     * 判断队列是否为空
     */
    public boolean isEmpety () {
        return front == rear;
    }

    /**
     * 队列是否满
     */
    public boolean isFull () {
        return size() >= maxSize ;
    }

    /**
     * 有效数据的个数
     */
    public int size() {
        return (rear+arrSize-front) % (arrSize);
    }

    /**
     * 向队列中添加数据
     */
    public void addQueue (int data) {
        if (isFull()) {
            System.out.println("队列已满");
            return;
        }
        queueArr[rear] = data;
        rear = (rear + 1)%arrSize;
     }

    /**
     * 取出队列数据
     */
    public int getQueue () {
        if (isEmpety()) {
            throw new RuntimeException("队列为空");
        }
        int arr = queueArr[front] ;
        front = (front + 1)%arrSize;
        return arr;
    }

    /**
     * 获取头数据
     */
    public int headQueue () {
        if (isEmpety()) {
            throw new RuntimeException("队列为空");
        }
        return queueArr[front];
    }

    /**
     * 显示队列数据
     */
    public void show () {
        if (isEmpety()) {
            System.out.println("队列为空");
            return;
        }
        for (int i = front ; i < front + size() ; i++) {
            System.out.println("arr["+i%(maxSize)+"]="+queueArr[i%(arrSize)]);
        }
    }

}

package queue;

import java.util.Scanner;

/**
 * @author lixiangxiang
 * @description 数组模拟队列
 * @date 2021/4/16 16:56
 */
public class ArrayQueueDemo {
    public static void main(String[] args) {
        //创建一个队列
        CircleQueue arrayQueue = new CircleQueue(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 's' :
                    arrayQueue.show();
                    break;
                case 'a' :
                    System.out.println("输入一个数");
                    int value = scanner.nextInt();
                    arrayQueue.addQueue(value);
                    break;
                case 'g' :
                    try {
                        int queueValue = arrayQueue.getQueue();
                        System.out.println("取出的数据是"+queueValue);
                    }
                    catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'h' :
                    try {
                        int res = arrayQueue.headQueue();
                        System.out.println("消息队列的头数据是"+res);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'e':
                    loop = false;
                    System.out.println("程序退出");
                    break;
                default:
                    break;
            }
        }
    }
    //显示队列所有数据
}

单链表

image-20210418113051179

image-20210418113421810

链表添加示意图

image-20210418163318386

  1. 先创建一个head头节点,作用就是表示单链表的头
  2. 后面我们每添加一个节点,就直接加入大链表的最后

遍历:

  1. 通过一个辅助变量,帮助遍历整个单链表

根据某个字段排序添加链表

image-20210419092258878

思路

  1. 首先先找到新添加的节点的位置,是通过辅助变量,通过遍历找到
  2. 新的节点.next = temp.next
  3. temp.next = 新的节点

修改链表

  1. 找到该节点
  2. 修改该节点的内容

删除节点

image-20210419112215562

单向链表代码实现

package linkedlist.single;

/**
 * @author lixiangxiang
 * @description /
 * @date 2021/4/18 16:56
 */
public class SingleLinkedList {
    //先初始化一个头节点,头节点不用动,不存放具体数据
    private HeroNode head = new HeroNode(0,"","");

    public void add (HeroNode node) {
        //head节点不能动,我们需要用一个辅助变量遍历temp
        HeroNode temp = head;
        //遍历链表,找到最后
        while (temp.next != null) {
            //如果没有找到最后,将temp后移
            temp = temp.next;
        }
        temp.next = node;
    }

    //查看节点
    public void show () {
        HeroNode temp = head;
        while (temp.next != null) {
            temp = temp.next;
            System.out.println(temp.toString());
        }
    }


    //排序添加节点
    public void addByOrder (HeroNode node) {
        //head节点不能动,我们需要用一个辅助变量遍历temp
        HeroNode temp = head;
        boolean flag = false;
        //遍历链表,找到最后
        while (true) {
            //链表为空
            if (temp.next == null) {
                break;
            }
            //链表下一节的no大于插入节点的no,说明在此节点后插入
            if (temp.next.no > node.no) {
                break;
            }
            //如果下一节点的no与当前节点no相同,说明该键已存在
            if (temp.next.no == node.no) {
                flag = true;
                break;
            }
            //如果没有找到最后,将temp后移
            temp = temp.next;
        }
        if (flag) {
            System.out.println("该键已存在");
            return;
        }
        node.next = temp.next;
        temp.next = node;
    }

    //修改节点
    public void update (HeroNode node) {
        HeroNode temp = head;
        boolean flag = false;
        while (temp.next != null) {
            if(temp.no == node.no) {
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if (!flag) {
            System.out.println("找不到key为"+node.no+"的节点");
            return;
        }
        temp.nickname = node.nickname;
        temp.name = node.name;
    }

    //删除节点
    public void delNode (int key) {
        HeroNode temp = head;
        boolean flag = false;
        while (temp.next != null) {
            if (temp.next.no == key) {
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if(!flag) {
            System.out.println("找不到key为"+key+"的节点");
            return;
        }
        temp.next = temp.next.next;
    }

    public HeroNode getHead() {
        return head;
    }
}

package linkedlist.single;

/**
 * @author lixiangxiang
 * @description 链表节点
 * @date 2021/4/18 16:41
 */
public 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 + '\'' +
                '}';
    }
}

腾讯面试题—反转链表

image-20210420082046626

百度面试题—从尾到头打印链表

  1. 方式1:将单链表进行反转操作,然后再遍历即可,这样做的问题时会破坏原来的单链表的结构,不建议
  2. 方式2:可以利用栈这个数据结构,将各个节点压入到栈中,然后利用栈的先进后出的特点,就实现了逆序打印的效果。

面试题代码实现

package linkedlist.single;

import java.util.Stack;

/**
 * @author lixiangxiang
 * @description 单链表面试题
 * @date 2021/4/19 17:04
 */

public class SingleLinkedListTest  {
    
    /**
     * description: 寻找倒数第n个节点
     *
     * @author: lixiangxiang 
     * @param heroNode 头结点
     * @param index 倒数第n个
     * @return linkedlist.single.HeroNode
     * @date 2021/8/9 10:37 
     */
    public  HeroNode findLastIndexNode (HeroNode heroNode, int index) {
        HeroNode temp = heroNode.next;
        int size = getNodeNum(heroNode);
        System.out.println(size);
        if (temp == null || index > size) {
            return null;
        }
        while (temp != null) {
            if (temp.no == size - index + 1) {
                break;
            }
            temp = temp.next;
        }
        return temp;
    }

    //获取单链表节点个数
    public int getNodeNum(HeroNode head) {
        if(head.next == null) {
            return 0;
        }
        int len = 0;
        HeroNode temp = head.next;
        while (temp != null ) {
            len++;
            temp = temp.next;
        }
        return len;
    }

    /**
     * 反转链表
     * @param heroNode /
     */
    public void reverseList (HeroNode heroNode) {
        HeroNode reverseHead = new HeroNode(0," "," ");
        HeroNode temp = heroNode.next;
        HeroNode next = null;
        if (heroNode.next == null || heroNode.next.next == null) {
            return;
        }
        while (temp != null) {
            //暂时保存当前节点的下一节点
            next = temp.next;
            //将temp的下一个节点指向新的链表的最前端
            temp.next = reverseHead.next;
            reverseHead.next = temp;
            //让temp后移
            temp = next;
            show(reverseHead);
            System.out.println("===========================");
        }
        heroNode.next = reverseHead.next;
    }

    public void show (HeroNode head) {
        HeroNode temp = head;
        while (temp.next != null) {
            temp = temp.next;
            System.out.println(temp.toString());
        }
    }

    /**
     * 反向打印
     * @param head 头节点
     */
    public void reversePrint(HeroNode head) {
        HeroNode cur = head.next;
        Stack<HeroNode> stack = new Stack<>();
        while (cur != null) {
            stack.push(cur);
            cur = cur.next;
        }
        while (stack.size() > 0) {
            HeroNode pop = stack.pop();
            System.out.println(pop);
        }
    }

    
    /**
     * description: 合并链表
     *
     * @author: lixiangxiang 
     * @param head1 头结点1
     * @param head2 头结点2
     * @return linkedlist.single.HeroNode
     * @date 2021/8/9 10:34 
     */
    public HeroNode mergeList (HeroNode head1, HeroNode head2) {
        HeroNode mergeList = new HeroNode(0,"","");
        HeroNode mergeCur = mergeList;
        HeroNode cur1 = head1.next;
        HeroNode cur2 = head2.next;
        while (cur1 !=null || cur2 !=null) {
            if (cur2 == null || (cur1!=null && cur1.no < cur2.no)) {
                mergeCur.next = cur1;
                cur1 = cur1.next;
            }else {
                mergeCur.next = cur2;
                cur2 = cur2.next;
            }
            mergeCur = mergeCur.next;
        }
        return mergeList;
    }
}

双向链表

image-20210420111044814

遍历、添加、修改、删除操作

  1. 遍历

    1)遍历和单项链表一样,不过可以倒序遍历

  2. 添加

    1)找到双向链表的最后一个节点

    2)temp.next = newHeroNode

    1. newHeroNode.pre = temp
  3. 修改

    与单链表相同

  4. 删除

    1. 可以实现自我删除
    2. 直接找到需要删除的节点,比如temp
    3. temp.pre.next = temp.next
    4. temp.next.pre = temp.pre
  5. 顺序添加

    1. 遍历链表

    2. 如果当添加的节点小于当前节点的下一节点时说明 添加的节点应该添加到当前节点后 即: ndoe.no < temp.next.no 还需考虑这是否是第一个节点

      如果是第一个节点,也按如下操作,所以条件为 temp.next == null || ndoe.no < temp.next.no

    3. node.pre = temp //node的前一节点 = 当前指针所在节点

    4. node.next = temp.next;

    5. temp.next.pre = node; //注意这里,当node插入后,原temp的下一个节点的pre应该改变,因为其之前连接的是temp,现在应该连接node。并要注意判断temp是否有下一个节点(即temp.node!=null)

    6. temp.next = node;

/**
 * 顺序添加
 * @param node
 */
public void addByOrder (HeroNode node) {
        HeroNode temp = head;
        while (temp != null) {
            if (node.no == temp.no) {
                System.out.println("该节点已存在");
                return;
            }
            if (temp.next == null || node.no < temp.next.no ) {
                node.pre = temp;
                node.next = temp.next;
                if (node.next != null) {
                    temp.next.pre = node;
                }
                temp.next = node;
                return;
            }
            temp = temp.next;
        }
}

约瑟夫环问题(单向环形链表)

image-20210420201433469

image-20210420205149759

约瑟夫环代码实现

package linkedlist.circle;

/**
 * @author lixiangxiang
 * @description /
 * @date 2021/4/20 20:56
 */
public class Boy {
    private int no;

    private Boy next;

    public Boy(int no) {
        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;
    }

    @Override
    public String toString() {
        return "Boy{" +
                "no=" + no +
                '}';
    }
}
package linkedlist.circle;


/**
 * @author lixiangxiang
 * @description 单向环形链表
 * @date 2021/4/20 20:24
 */
public class CircleSingleLinkedList {
    private Boy first = new Boy(-1);
    //添加小孩节点,构建一个环形的链表
    public void addBoy(int num) {
        if (num < 1) {
            System.out.println("数量不能小于1");
            return;
        }
        //辅助指针
        Boy current = null;
        for (int i = 1; i <= num; i++) {
            Boy boy = new Boy(i);
            if (i == 1) {
                first = boy;
                first.setNext(first);
                current = first;
            }else {
                current.setNext(boy);
                boy.setNext(first);
                current = current.getNext();
            }
        }
    }

    //遍历环形单链表
    public void show () {
        if (first == null) {
            System.out.println("链表为空");
            return;
        }
        //辅助指针
        Boy current = first;
        while (true) {
            System.out.println(current);
            if (current.getNext() == first) {
                break;
            }
            current = current.getNext();
        }
    }

    /**
     * description: 获取出圈的顺序
     *
     *
     * @author: lixiangxiang
     * @param start 开始位置
     * @param nums 总数
     * @param times 每次查多少个数
     * @return void
     * @date 2021/4/20 22:41
     */
    public void getCount (int start, int nums, int times) {
        if (start < 1 || nums < 1 || times < 1 || start > nums) {
            System.out.println("输入参数有误");
            return;
        }
        //创建辅助指针
        Boy helper = first;
        //让辅助指针指向最后一个
        while (helper.getNext() != first) {
            helper = helper.getNext();
        }
        //查数前先让first和helper回到开始位置
        for (int i = 0; i < start - 1; i++) {
            helper = helper.getNext();
            first = first.getNext();
        }
        //开始查数 first和helper同时移动m - 1个位置,然后该位置孩子出圈
        while (first != helper) {
            for (int i = 0; i < times - 1; i++) {
                helper = helper.getNext();
                first = first.getNext();
            }
            //孩子出圈
            System.out.println("出圈孩子为"+ first.getNo());
            first = first.getNext();
            helper.setNext(first);
        }
        System.out.println("最后一个孩子为"+first.getNo());
    }
}
package linkedlist.circle;

/**
 * @author lixiangxiang
 * @description /
 * @date 2021/4/20 21:46
 */
public class CircleSingleLinkedDemo {


    public static void main(String[] args) {
        CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
        circleSingleLinkedList.addBoy(10);
        circleSingleLinkedList.getCount(2,10,3);
    }
}

image-20210421160214657

介绍

  • 栈是一个先入后出的有序列表

  • 栈是限制线性表中原酸的插入和删除只能在线性表同一端进行的一种特殊线性表。允许插入和删除的一端,为变化的一端,成为栈顶(TOP),另一端为固定的一端,成为栈底(Bottom)

  • 根据栈的定义可知,最先放入占中元素在栈底,最后放入的元素在栈顶,而删除元素刚好相反,最后放入的元素最先删除,最先放入的最后删除。

image-20210421163641398

数组模拟栈

  1. 定义一个top来表示栈顶,初始化为 -1
  2. 入栈的操作,当有数据加入到栈时,top++;stack[top] = data
  3. 出栈的操作,int value = stack[top];top–;return value
代码实现
package stack;

/**
 * @author lixiangxiang
 * @description /
 * @date 2021/4/21 16:43
 */
public class ArrayStackDemo {
    //存放栈数据的数组
    private int arr[];
    //指针
    private int pointer;
    //栈的大小
    private int size;

    public ArrayStackDemo(int size) {
        this.size = size;
        this.arr = new int[size];
        this.pointer = -1;
    }


    /**
     * 判断是否栈满
     */
    public boolean isFull () {
        if (pointer == size - 1) {
            return true;
        }
        return false;
    }

    /**
     * 判断是否栈空
     * @return
     */
    public boolean isEmpty() {
        if (pointer == -1) {
            return true;
        }
        return false;
    }

    /**
     * description: 向栈中存入数据
     *
     * @author: lixiangxiang
     * @param num
     * @return void
     * @date 2021/4/21 17:17
     */
    public void push (int num) {
        if (isFull()) {
            System.out.println("栈内存已满");
            return;
        }
        pointer ++;
        arr[pointer] = num;
    }

    /**
     * 出栈
     * @return
     */
    public int pop () {
         if (isEmpty()) {
             throw new RuntimeException("栈内存为空");
         }
         int num = arr[pointer];
         pointer --;
         return num;
    }

    /**
     * 显示栈
     */
    public void show() {
        if (isEmpty()) {
            System.out.println("栈内存为空");
            return;
        }
        for (int i = pointer; i >= 0; i--) {
            System.out.println("arr["+i+"] = " +arr[i]);
        }
    }
}



栈实现综合计算器

image-20210422090723773

image-20210422145230672

image-20210422145252970

image-20210422145830853

image-20210422145915539

image-20210422161046191

image-20210422161801990

代码实现
package stack.polandNotation;

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

/**
 * @author lixiangxiang
 * @description 逆波兰表达式计算器
 * @date 2021/4/22 20:06
 */
public class PolandNotation {
    public static void main(String[] args) {
        //中缀表达式转后缀表达式
        String expression = "12+((2+3)*4)-5";
        //将数字和运算符分开放入集合中
        List<String> expressionList = toExpressionList(expression);
        System.out.println(expressionList);
        //中缀表达式转后缀表达式
        List<String> suffixExpressionList = parseSuffixExpressionList(expressionList);
        System.out.println(suffixExpressionList.toString());
        int res = calcSuffixExpression(suffixExpressionList);
        System.out.println("结果为" + res);
    }

    private static List<String> toExpressionList(String expression) {
        String[] split = expression.split("");
        List<String> expressionList = new ArrayList<>();
        StringBuilder str = new StringBuilder();
        for (int i = 0; i < split.length; i++) {
            if (split[i].matches("\\d+")) {
                str.append(split[i]);
                if (i + 1 == split.length) {
                    expressionList.add(str.toString());
                    break;
                }
                if (split[i + 1].matches("[\\+\\-\\/\\*\\(\\)]")) {

                    expressionList.add(str.toString());
                    str = new StringBuilder();
                }
            } else {
                expressionList.add(split[i]);
            }
        }
        return expressionList;
    }

    /**
     * 中缀表达式转后缀表达式
     *
     * @param expressionList /
     * @return
     */
    public static List<String> parseSuffixExpressionList(List<String> expressionList) {

        Stack<String> s1 = new Stack<>();
        //创建一个数组最终结果,刚开始存的是符号
        List<String> s2 = new ArrayList<>();

        //如果是数字则直接存放入数据栈
        for (String data : expressionList) {
            if (data.matches("\\d+")) {
                s2.add(data);
            }
            //如果是左括号,直接将运算符压入s1栈中
            else if ("(".equals(data)) {
                s1.push(data);
            }
            //如果是右括号,依次弹出s1栈顶的运算符取出并压入到s2中,直到遇到左括号为止
            else if (")".equals(data)) {
                while (!"(".equals(s1.peek())) {
                    s2.add(s1.pop());
                }
                //然后将左括号丢弃
                s1.pop();
            }
            //如果是运算符,比较其与s1栈顶运算符的优先级
            else {
                //如果为s1栈为空直接压入s1
                if (s1.isEmpty()) {
                    s1.add(data);
                }
                //比较与s1栈顶运算符的优先级,如果优先级大于s1栈顶优先级,压入s1栈中
                else if (level(data) > level(s1.peek())) {
                    s1.push(data);
                }
                //如果优先级小于等于s1栈顶优先级,则将s1栈顶运算符弹出压入s2中,再次与新的栈顶运算符比较
                //直到data运算符优先级大于s1栈顶优先级 或者s1栈为空为止
                else {
                    while (!s1.isEmpty() && level(data) <= level(s1.peek())) {
                        s2.add(s1.pop());
                    }
                    s1.push(data);
                }
            }
        }
        while (!s1.isEmpty()) {
            s2.add(s1.pop());
        }
        return s2;
    }

    /**
     * 判断操作符等级
     */
    public static int level(String operate) {
        if ("*".equals(operate) || "/".equals(operate)) {
            return 1;
        } else if ("+".equals(operate) || "-".equals(operate)) {
            return 0;
        }
        return -1;
    }

    /**
     * 计算后缀表达式结果
     */
    public static int calcSuffixExpression(List<String> suffixExpressionList) {
        Stack<Integer> numStack = new Stack<>();
        int result = 0;
        for (String data : suffixExpressionList) {
            if (data.matches("\\d+")) {
                numStack.push(Integer.parseInt(data));
            } else {
                Integer num1 = numStack.pop();
                Integer num2 = numStack.pop();
                result = calc(num1, num2, data.charAt(0));
                numStack.push(result);
            }
        }
        return result;
    }

    /**
     * 计算
     */
    private static int calc(int num1, int num2, int operate) {
        switch (operate) {
            case '+':
                return num1 + num2;
            case '-':
                return num2 - num1;
            case '/':
                return num2 / num1;
            case '*':
                return num1 * num2;
            default:
                throw new RuntimeException("操作符输入有误");
        }
    }
}

递归

递归的概念:

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

image-20210423090651691

递归需要遵循的规则

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

递归——迷宫问题

image-20210423103139813

/**
 * @author lixiangxiang
 * @description 迷宫问题
 * @date 2021/4/23 9:23
 */
public class Maze {
    public static void main(String[] args) {
        //创建地图
        int[][] map = createMap();
        showMap(map);
        //走迷宫
        runMaze(map,1,1);
        System.out.println("=================");
        showMap(map);
    }

    /**
     * description: 创建地图
     *规定 1代表障碍物,0代表可以走未走过的路,2代表已走过的路,3代表走不通的路
     * @author: lixiangxiang
     * @return void
     * @date 2021/4/23 10:53
     */
    private static int[][] createMap() {
        int [][] map = new int[8][7];
        for (int i = 0; i < map.length; i++) {
            map[i][0] = 1;
            map[i][map[0].length-1] = 1;
        }
        for (int i = 0; i < map[0].length; i++) {
            map[0][i] = 1;
            map[map.length-1][i] = 1;
        }
        map[3][1] = 1;
        map[3][2] = 1;
        return map;
    }

    private static void showMap(int[][] map){
        for (int[] row : map) {
            for (int data : row) {
                System.out.print(data+" ");
            }
            System.out.println();
        }
    }

    /**
     * description: 走路顺序为:下右上左
     *
     * @author: lixiangxiang
     * @param map 地图
     * @param x x坐标
     * @param y y坐标
     * @return boolean
     * @date 2021/4/23 11:06
     */
    private static boolean runMaze(int[][]map,int x,int y) {
        int end = map[6][5];
        if( end == 2) {
            return true;
        }
        //如果下一节点没走过
        else if (map[x][y] == 0){
            //先假设可以走
            map[x][y] = 2;
            //如果往下走可以
            if (runMaze(map,x+1,y)) {
                return true;
            }
            //如果右走可以
            else if (runMaze(map,x,y+1)) {
                return true;
            }
            //如果上走
            else if (runMaze(map,x-1,y)) {
                return true;
            }
            //如果都不行 回溯
            else {
                //标记该点不能走
                map[x][y] = 3;
                return false;
            }
        }
        //如果下一节点是 1或2或3 不能走
        else {
            return false;
        }
    }
}

八皇后问题

八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯●贝瑟尔于1848年提出:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即:任意两个皇后都不能处于同一行、同一列或同-斜线上,问有多少种摆法(92)。

八皇后问题算法思路分析

  • 第一个皇后先放第一行第一-列
  • 第二个皇后放在第二行第一-列、然后判断是否OK,如果不 OK,继续放在第二列、第三列、依次把所有列都放完,找到一个合适
  • 继续第三个皇后,还是第一-列、第二列…直到第8个皇后也能放在一一个不冲突的位置, 算是找到了一个正确解
  • 当得到一个正确解时,在栈回退到上一-个栈时,就会开始回溯,即将第一个皇后, 放到第一列的所有正确解,全部得到.
  • 然后回头继续第- -个皇后放第二列,后面继续循环执行1,2.3,4 的步骤A .
代码实现
package recursion;

/**
 * @author lixiangxiang
 * @description 八皇后
 * @date 2021/4/25 11:01
 */
public class Queen8 {
    int size = 8;
    int[] arr = new int[size];
    static int count = 0;
    public static void main(String[] args) {
        new Queen8().check(0);
        System.out.println(count);
    }

    /**
     * 放置第n皇后
     */
    public void check (int n) {
        if (n == size)  {
            print();
            return;
        }
        //依次放入皇后
        for (int i = 0; i < size; i++) {
            //先把当前的皇后n,放到该行的第一列
            arr[n] = i;
            //判断当放置到第n个皇后到i时,是否冲突
            //不冲突
            if (judge(n)) {
                //判断下一个皇后
                check(n+1);
            }
            //如果冲突,就继续执行arr[n] = i;让皇后移至本行的下一列
        }
    }

    /**
     * 判断第n个棋子与之前的棋有没有冲突
     */
    public boolean judge (int n) {
        for (int i = 0; i < n; i++) {
            //arr[i] == arr[n] 判断n与之前的皇后是否在同一列
            //Math.abs(n - i) == Math.abs(arr[n] - arr[i]) 判断n与之前的皇后是否在同一斜线
            if (arr[i] == arr[n] || Math.abs(n - i) == Math.abs(arr[n] - arr[i])) {
                return false;
              }
        }
        return true;
    }

    public void print () {
        count++;
        for (int data : arr) {
            System.out.print(data+" ");
        }
        System.out.println();
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值