Java数据结构栏目总结

news2024/9/21 18:55:20

 目录

数组与稀疏数组

队列:自己用数组模拟Queue

环形队列,取模【取余】实现.

单链表(LinkList)

双向链表(Next 、Pre)

单向环形链表


线性结构

  • 数组与稀疏数组

稀疏数组,很多0值,可用于压缩

特点:共n+1行3列,n为不同值的个数(0除外)

第一行:数组的行数、列数、不同值的个数

第二行:行号、列号、值

public class SparseArray {

    public static void main(String[] args) {
        convert();

    }
    private static void convert(){
//        01.array2sparse
        //定义7*7的二维数组,arr[6][6]=7、arr[2][2]=3 、arr[4][4]=5,其余位置为0
        int[][] arr = new int[7][7];//别😅,7为行列数不是索引!

        arr[2][2]=3;
        arr[4][4]=5;
        arr[6][6]=7;
        //增强for遍历
        for (int[]row:arr) {
            for (int i :row) {
                System.out.printf("%d\t",i);
            }
            System.out.println();
        };
        //获取非0值个数确定数组行数
        int notzeroNumber=0;
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr.length; j++) {
                if(arr[i][j]!=0){

                    notzeroNumber=notzeroNumber+1;
                }
            }
        }

        int count = 0;//稀疏数组中第几行
        int[][] arr2 = new int[notzeroNumber+1][3];
        arr2[0][0]= arr.length;
        arr2[0][1]=arr.length;
        arr2[0][2]=notzeroNumber;
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr.length; j++) {
                if(arr[i][j]!=0){
                    count++;
                    arr2[count][0]=i;
                    arr2[count][1]=j;
                    arr2[count][2]=arr[i][j];
                }
            }
        }
        //遍历打印稀疏数组
        for (int[]row:arr2) {
            for (int i :row) {
                System.out.printf("%d\t",i);
            }
            System.out.println();
        };

//        02sparse2array
//        获取数组行列数
        int[][] arr3 =new int[arr2[0][0]][arr2[0][1]];
//          设置数组中非0值
        for (int i = 1; i < arr2.length; i++) {
            arr3[arr2[i][0]][arr2[i][1]]=arr2[i][2];
        }
        for (int[]row :arr3) {
            for (int i :row) {
                System.out.printf("%d\t",i);
            }
            System.out.println();
        }

    };

}

***

  • 队列

自己用数组模拟Queue

public class Queue {
    private int maxSize;
    private int front;
    private int rear;
    private int[]arr;//利用数组模拟!你搞得数组都没有

    public Queue(int maxSize) {
        this.maxSize = maxSize;
        arr = new int[this.maxSize];
        front=-1;
        rear=-1;
    }
    public void addQueue(int n){
        //先判断队列是否已满
        if(isFull()){
            System.out.println("队列满了哦~");
        }
        //rear初始值为-1
        rear = rear+1;
        arr[rear]=n;
    }
    public  int getQueue(){
        //先判断队列是否为空
        if (isEmpty()){
            throw new RuntimeException("队列为空");
        }
        front++;
        return arr[front];//front默认为-1,见构造函数
    }
    public  void showQueue(){
        if (isEmpty()){
            throw new RuntimeException("队列为空");
        }
        for (int i = 0; i < arr.length; i++) {
            System.out.printf("arr[%d]=%d\n",i,arr[i]);//%d十进制整型
        }
    }
    public  int getHeader(){
        if (isEmpty()){
            throw new RuntimeException("队列为空");
        }
        return arr[front+1];
    }
    public boolean isFull(){
        return rear==maxSize-1;
    }
    public boolean isEmpty(){
        return rear==front;
    }
    public int getMaxSize() {
        return maxSize;
    }

    public void setMaxSize(int maxSize) {
        this.maxSize = maxSize;
    }

    public int getFront() {
        return front;
    }

    public void setFront(int front) {
        this.front = front;
    }

    public int getRear() {
        return rear;
    }

    public void setRear(int rear) {
        this.rear = rear;
    }
}

key:

1.通过构造函数初始化队列

public Queue(int maxSize) {
    this.maxSize = maxSize;
    arr = new int[this.maxSize];
    front=-1;
    rear=-1;
}

2.规定:front=rear初始化值为-1,取头元素时为arr[rear+1]

3.队列为空【front==rear】、队列满时条件[rear=maxsize-1]

问题:目前数组使用一次就不能用,没有达到复用的效果

附加:环形队列,取模【取余】实现,优化解决上述问题.

key:why取模,见下图

size=(rear-front+max)%max

full:(rear+1)%max==front

empty:rear==front

show:for(i=front,i<front+size,i++){

      "arr[%d]=d%\n",i%maxSize,arr[i%maxSize]

}

public class CircleQueue {
    private int maxSize;
    private int front;
    private int rear;//rear 指向队列的最后一个元素的后一个位置
    private int[]arr;

    public CircleQueue(int maxSize) {
        this.maxSize = maxSize;
        front=0;
        rear=0;
        arr=new int[maxSize];
    }
    public void addQueue(int a){
        if (isFull()){
            System.out.println("已满");
            return;
        }
        //rear指向队列的最后一个元素的后一个位置
        arr[rear]=a;//先赋值
        rear=(rear+1)%maxSize;//后++
    };
    public int getQueue(){
        if(isEmpty())
        {
            throw new RuntimeException("队列为空");
        }
        // 这里需要分析出 front 是指向队列的第一个元素
        // 1. 先把 front 对应的值保留到一个临时变量
        // 2. 将 front 后移, 考虑取模
        // 3. 将临时保存的变量返回
        int value =arr[front];
        front=(front+1)%maxSize;
        return  value;
    }
    public int getHeader(){
        if(isEmpty()){
            throw new RuntimeException("空的");
        }
        return arr[front];
    }
//key!考虑front在后面
    public void showQueue(){
        if(isEmpty()){
            throw new RuntimeException("空的");
        }
        //遍历!!!取模思想,front也是变的【取了的】
        for (int i = front; i < front+size(); i++) {
            System.out.printf("arr[%d]=d%\n",i%maxSize,arr[i%maxSize]);
        }
    }

    public  boolean isEmpty(){
        return front==rear;
    }
    public boolean isFull(){

        return (rear + 1 ) % maxSize==front;
    }
    public  int size(){

        return (rear+maxSize-front)%maxSize;
    }
    public int getMaxSize() {
        return maxSize;
    }

    public void setMaxSize(int maxSize) {
        this.maxSize = maxSize;
    }

    public int getFront() {
        return front;
    }

    public void setFront(int front) {
        this.front = front;
    }

    public int getRear() {
        return rear;
    }

    public void setRear(int rear) {
        this.rear = rear;
    }

    public int[] getArr() {
        return arr;
    }

    public void setArr(int[] arr) {
        this.arr = arr;
    }
}

单链表(LinkList)

//链表的Node节点
public class Node {
    private  int no;
    private String name;
    private String nickname;
    private  Node next;

    public Node(int no, String name, String nickname) {
        this.no = no;
        this.name = name;
        this.nickname = nickname;
    }
}
public class LinkList {
    //头节点属性
   private Node headNode = new Node(0,"","");
//   01添加节点
//    1.找到当前链表最后节点
//    2.将最后节点next指向新的节点
    public void add(Node node){
        //思想:
        //头节点不能动,因此需借助一个辅助变量temp
        Node temp = headNode;
        while(true){
            if (temp.getNext() == null){
                break;
            }
            //如果有就后移
            temp = temp.getNext();
        }
        temp.setNext(node);
    };
    //02循环遍历打印节点
    public void show(){
        if (headNode.getNext()==null){
            System.out.println("链表为空");
            return;
        }
        Node temp = headNode.getNext();
        while (true){
            if (temp==null){
                break;
            }
            System.out.println(temp);
            temp=temp.getNext();
        }
    }
    //03按顺序编号插入节点
    public void addbyOrder(Node node){
        Node temp = headNode;
        boolean flag = false;
        while (true){
            if(temp.getNext()==null){
                break;
            }
            if(temp.getNext().getNo()>node.getNo()){
                break;
            }else if (temp.getNext().getNo()==node.getNo()) {
                flag=true;
                break;
            }
             temp = temp.getNext();
        }
        if(flag){
            System.out.printf("准备插入的英雄的编号 %d 已经存在了\n",node.getNo());
        }else{
            //改变相应节点的next指针
            node.setNext(temp.getNext());
            temp.setNext(node);
        }
    }
    //04修改节点信息
    //根据节点编号no修改节点信息【no编号不能改】
    public void updateNode(Node node){
        if (headNode.getNext()==null){
            System.out.println("链表为空~");
            return;
        }
        Node temp = headNode.getNext();
        boolean flag=false;//表示是否找到该节点,思想,代码分离
        while (true){
            if(temp==null){
                break;
            }
            if(temp.getNo()==node.getNo()){
                flag=true;
                break;
            }
            temp=temp.getNext();
        };
        if (flag){
            temp.setName(node.getName());
            temp.setNickname(node.getName());
        }
    }
    //05删除节点,根据编号no删除指定节点
    public void deleteNode(int no){
        Node temp = headNode;
        boolean flag = false;//是否找到所删节点
        if(temp.getNext()==null){
            System.out.println("空链表");
            return;
        }
        while (true){
            if(temp.getNext()==null){
                System.out.println("空链表");
                break;
            }
            if (temp.getNext().getNo()==no){
                flag=true;
                break;
            }
            temp=temp.getNext();
        }
        if (flag){
            //修改next指针
            temp.setNext(temp.getNext().getNext());
        }

    }

链表反转三种方式:

//01借助pre\cur\next反转链表,需注意的是反转后需改变头指针的指向
    public void reverseList(){
        if(headNode.getNext()==null||headNode.getNext().getNext()==null){
            return;
        }
        Node pre = null;
        Node cur =headNode.getNext();
        Node next =null;
        while (cur!=null){
            next=cur.getNext();
            cur.setNext(pre);
            pre=cur;
            cur=next;
        }
        //头节点指针需指向反转后的首节点
        // 仍指向反转前的首节点会导致后面的节点被垃圾回收【没有指针指向】
        headNode.setNext(pre);
    }
    //02创建新指针头节点
//    思路
//            1)创建一个新的头指针newHead。
//            2)创建一个结点p遍历反转前的链表,每遍历一个结点,把newHead指向当前结点。同时创建一个结点cur,用来保存p的下一个结点,以此来保证p遍历整个反转前的链表。
//            3)最后原来头指针head,指向新的链表。

    public void reverseList2(){
        Node cur = headNode.getNext();
        Node next=null;
        Node reverseHead = new Node(0,"","");
        if (cur==null||cur.getNext()==null){return;}
        while (cur!=null){
            next=cur.getNext();
            cur.setNext(reverseHead.getNext());
            reverseHead.setNext(cur);
            cur=next;
        }
        headNode.setNext(reverseHead.getNext());//指向当前链表
    }

    //本函数的headNode参数为链表的第一个元素,本函数返回逆序的第一个节点(也就是原来的倒数第一个节点)
    // 需将headNodey头指针指向函数返回值
    public Node reverseList3(Node head){
       if(head==null||head.getNext()==null){
           return head;
       };
      Node newNode = reverseList3(head.getNext());//初次返回链表最后一个节点,条件为headNode为倒数第二个节点时
      head.getNext().setNext(head);
      head.setNext(null);
      headNode.setNext(newNode);
      return newNode;//返回上一层的递归函数,保证指针指向反链表头部
    }

    public void reversePrint(){
        if (headNode.getNext()==null){return;}
        Stack<Node> stack = new Stack<Node>();
        Node cur = headNode.getNext();
        while (cur!=null){
            stack.add(cur);
            cur=cur.getNext();
        }
        while (stack.size()>0) {
            System.out.println(stack.pop());
        }
    }

  • 双向链表(Next 、Pre)

按顺序插入

keys:

1.temp=headNode考虑到添加节点为最前面的节点

2.temp.getNext()!=null考虑到添加节点是否为尾节点

3.设置双向链表首先设置Node指向其他节点,以防添加节点为首节点temp为headNode指向Node,导致原本链表直接垃圾回收

node.setNext(temp.getNext())

if(temp.getNext()!=null){temp.getNext().setPre(node)}

temp.setNext(Node)

Node.setPre(temp)
//03按顺序编号插入节点!!!
    //插入节点总与前一节点《=》,后一节点需考虑
    public void addbyOrder(Node node){
        Node temp = headNode;
        boolean flag = false;
        while (temp.getNext()!=null){
            if(temp.getNext().getNo()>node.getNo()){
                break;
            }else if (temp.getNext().getNo()==node.getNo()) {
                flag=true;
                break;
            }
            temp = temp.getNext();
        }
        if(flag) {
            System.out.printf("准备插入的英雄的编号 %d 已经存在了\n", node.getNo());}
       else{

            //改变顺序,头节点指向都变了,可能导致垃圾回收和【循环链表】
//            temp.setNext(node);
//            node.setPre(temp);
//            node.setNext(temp.getNext());
//            if(temp.getNext()!=null){
//                temp.getNext().setPre(node);
//            }
            //the order matters!
           node.setNext(temp.getNext());
            if(temp.getNext()!=null){
                temp.getNext().setPre(node);
            }
           temp.setNext(node);
            node.setPre(temp);

       }
    }

删除:考虑是否为尾节点

        if (flag){
            cur.getPre().setNext(cur.getNext());
            //判断删除节点是否为最后一个节点
            if(cur.getNext()!=null) {
                cur.getNext().setPre(cur.getPre());
            }
        }
  • 单向环形链表

1.环形链表生成

    //生成环
    private Boy first = null;
    public void addBoy(int num){
        if(num<1){
            System.out.println("num值不正确");
            return;
        }
        Boy curBoy = null;
        for (int i = 1; i <=num ; i++) {
            Boy boy =new Boy(i);
            if(i==1){
                //一个自己形成环
                first=boy;
                boy.setNext(first);
                curBoy=boy;
            }else {
                //形成环
                curBoy.setNext(boy);
                boy.setNext(first);
                curBoy=boy;
            }
        }

2.Josephu问题

/**
     *
     * @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("para_Error");
            return;
        }
        Boy helper= first;
        //helper转圈圈直至转到链表最后
        //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;}
            for (int i = 0; i < countNum-1; i++) {
                first=first.getNext();
                helper=helper.getNext();
            }
            System.out.printf("小孩%d出圈\n",first.getNo());
            first=first.getNext();
            helper.setNext(first);
        }
        System.out.printf("最后留在圈中的小孩编号%d\n",first.getNo());
    }

 目录

1.栈 

1.1基本概念 

​编辑

 1.2应用场景

1.3数组模拟栈

2.中序计算器 

3.中序转后序

4.逆波兰计算器 


1.栈 

1.1基本概念 

 1.2应用场景

1.3数组模拟栈

public class ArrayStack {
    private int maxSize;
    private int [] stack;
    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++;//初始为-1,所以先++
        stack[top]=value;
    }
    //出栈
    public int pop(){
        if (isEmpty()){
            throw new RuntimeException("栈空,没有数据");
        }
        int value = stack[top];
        top--;
        return value;
    }
    public void list(){
        if (isEmpty()){
            System.out.println("栈空,没有数据");
            return;
        }
        for (int i = top; i >=0 ; i--) {
            System.out.printf("stack[%d]=%d\n",i,stack[i]);

        }
    }
    //返回运算符的优先级,优先级是程序员来确定,优先级使
    //数字越大,则优先级就越高.
    //'',int类型?
    //自动类型转换:由低字节向高字节的转换 byte->short-> char –>int->long->float->double
    //char->int,ASCII,*(42),0(48),A(65),a(97)
    public int priority(int oper){
        if (oper=='*'||oper=='/'){
            return 1;
        } else if (oper=='+'||oper=='-') {
            return 0;
        }else {
            return -1;
        }
    }
    //判断是否为运算符
    public boolean isOpera(char val){
        return val=='+'||val=='-'||val=='*'||val=='/';
    }
    //弹出栈顶
    public int peek(){
        return stack[top];
    }

    public int cal(int num1 , int num2,int oper){
        int res = 0;
        switch (oper){
            case '+':
                res=num1+num2;
                break;
            case '-':
                res =num2 - num1;// 注意顺序break;
            case '*':
                res =num1*num2;
                break;
            case '/':
                res =num2 / num1;// 注意顺序break;
                break;
            default:
                break;
        }
        return res;
    }
}

2.中序计算器 

//多多看看
public class Calculator {
    //中序计算器
    public static void main(String[] args) {
        String expression = "7*2*2-5+1-5+3-4";
        ArrayStack numStack = new ArrayStack(10);
        ArrayStack operStack = new ArrayStack(10);
        int index = 0;//用于扫描
        int num1 =0;
        int num2 =0;
        int oper = 0;
        int res = 0;
        char ch = ' ';//每次扫描得到char保存至ch
        String keepNum="";//用于拼接多位数
        //开始循环扫描expression
        while (true){
            //依次得到每个字符
            ch = expression.substring(index,index+1).charAt(0);
            if (operStack.isOpera(ch)){//如果是运算符
                //符号栈中的符号优先级大于需加入的符号时,先pop两个数计算,计算结果push进numStack
                if (!operStack.isEmpty()){
                    if (operStack.priority(ch)<=operStack.priority(operStack.peek())){
                        num1=numStack.pop();//栈顶,"-"或“/”后面,“被减或被除”
                        num2=numStack.pop();//次栈顶
                        oper=operStack.pop();
                        res = numStack.cal(num1,num2,oper);
                        numStack.push(res);
                        operStack.push(ch);
                    }
                }else {
                    //需加入的符号优先级大于符号栈中的符号时
                    operStack.push(ch);
                }
            }else {
                //1.当处理多位数时,不能发现是一个数就立即入栈,因为他可能是多位数
                //2.在处理数,需要向expression的表达式的index后再看一位,如果是数就进行扫描,如果是符号才入栈
                //3.因此我们需要定义一个变量字符串,用于拼接处理多位数

                //处理多位数
                keepNum+=ch;

                //如果ch是表达式最后一位就直接入栈
                if (index==expression.length()-1){
                    numStack.push(Integer.parseInt(keepNum));
                }else {
                    //判断下一位字符是不是数字,如果是数字就继续扫描,如果是运算符就入栈
                    //注意是看后一位,不是index++
                    if (operStack.isOpera(expression.substring(index+1,index+2).charAt(0))){
                        numStack.push(Integer.parseInt(keepNum));
                        //keepNum清空!!!!
                        keepNum="";
                    }
                }
            }

            //index++,并判断是否扫描到expression最后
            index++;
            if (index>=expression.length()){
                break;
            }
        }
        //当表达式扫描完毕,就顺序的从数栈和符号栈中pop出相应的数和符号,并运行
        while (true){
            //符号栈为空时
            if (operStack.isEmpty()){
                break;
            }
            num1 = numStack.pop();
            num2 = numStack.pop();
            oper = operStack.pop();
            res = numStack.cal(num1,num2,oper);
            numStack.push(res);
        }

        //将数栈最后数,pop出,就是结果
        int res2 = numStack.pop();
        System.out.printf("表达式%s=%d",expression,res2);
    }
}

3.中序转后序

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

        String expression = "1+((2+3)*10)-5";
        List<String> infixExpressionList =toInfixExpressionList(expression);
        System.out.println("中缀表达式对应List"+infixExpressionList);
        List<String> suffixExpresssionList =parseSuffixExpresssionList(infixExpressionList);
        System.out.println(suffixExpresssionList);
        int result =calculate(suffixExpresssionList);
        System.out.println(result);

    }
    //将中缀表达式转换为
    public static List<String> toInfixExpressionList(String s){
        List<String> ls =new ArrayList<String>();
        int i = 0;
        String str;
        char c;
        do {
            //非数字,ASCII 48-57对应0-9
            if ((c=s.charAt(i))<48||(c=s.charAt(i))>57){
                ls.add(c+"");

                i++;//i往后移
            }else {
                str="";
                //考虑到了为,例如:12多个数
                while (i<s.length()&&(c=s.charAt(i))>=48&&(c=s.charAt(i))<=57){
                    str+=c;
                    i++;
                }
                ls.add(str);
            }
        }while (i<s.length());
            return ls;
    }
    public static List<String> parseSuffixExpresssionList(List<String>ls){
        Stack<String> s1 =new Stack<String>();//符号栈
        List<String> s2 = new ArrayList<String>();//存储中间结果
        for (String item:ls){
            System.out.println(item);
            //正则表达式,匹配【多个】数字
            if (item.matches("\\d+")){
                s2.add(item);
            } else if (item.equals("(")) {
                s1.push(item);
            } else if (item.equals(")")) {
                //为未匹配到(,前面的数pop到s2
                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);
            }
        }
        //将s1中剩余的符号压入s2中
            while (s1.size()!=0){
                s2.add(s1.pop());
            }
        return s2;//注意这是将其存入List,顺序输出就是后缀表达式对应的List
    }
public 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;
    }
}

4.逆波兰计算器 

 public static int calculate(List<String>ls){
        Stack<String> stack = new Stack<String>();
        for (String item :ls){
            //正则表达式,匹配数字
            if (item.matches("\\d+")){
                stack.push(item);
            }else {
                int num2 = Integer.parseInt(stack.pop());//栈顶,后面的数”被减、被除“
                int num1 = Integer.parseInt(stack.pop());
                int res =0;
                if (item.equals("+")){
                    res=num1+num2;
                } else if (item.equals("-")) {
                    res = num1-num2;
                } else if (item.equals("*")) {
                    res = num1*num2;
                } else if (item.equals("/")) {
                    res=num1/num2;
                }else {
                    throw new RuntimeException("运算符有误");
                }
                stack.push(""+res);
            }
        }
        return Integer.parseInt(stack.pop());
    }

}

  树

 1.why:

数组&链表&树

2. 大纲

2.1前中后序

public class HeroNode {
    private int no;
    private String name;
    private  HeroNode left;//默认为null
    private HeroNode right;//默认为null

    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 + '\'' +
                '}';
    }
    //遍历
    //编写前序遍历方法,被谁调用this就是谁
    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);
    };

  

}

2.2查找节点

 //查找
    //前序查找
    public HeroNode preSearch(int HeroNo){
        //判断当前节点是不是
        if (this.getNo()==HeroNo){
            return this;
        };
        //左子树前序查找
        //如果左递归前序查找节点,找到结点,则返回
        HeroNode resNode = null;//辅助节点

        if (this.getLeft()!=null) {
            resNode =this.getLeft().preSearch(HeroNo);
        }
        //resNode不为空才返回,因为右子树仍未查找
        if (resNode!=null){
            return resNode;
        }
        if (this.getRight()!=null){
            resNode = this.getRight().preSearch(HeroNo);
        }
        return resNode;
    }

    //中序查找
    public HeroNode infixSearch(int HeroNo){

        //左子树前序查找
        //如果左递归前序查找节点,找到结点,则返回
        HeroNode resNode = null;//辅助节点

        if (this.getLeft()!=null) {
            resNode =this.getLeft().preSearch(HeroNo);
        }
        //resNode不为空才返回,因为右子树仍未查找
        if (resNode!=null){
            return resNode;
        };
        //判断当前节点是不是
        if (this.getNo()==HeroNo){
            return this;
        }
        //查找右子树
        if (this.getRight()!=null){
            resNode = this.getRight().preSearch(HeroNo);
        }
        return resNode;
    }

    //后序查找
    public HeroNode postSearch(int HeroNo){
        //左子树前序查找
        //如果左递归前序查找节点,找到结点,则返回
        HeroNode resNode = null;//辅助节点
        if (this.getLeft()!=null) {
            resNode =this.getLeft().preSearch(HeroNo);
        }
        //resNode不为空才返回,因为右子树仍未查找
        if (resNode!=null){
            return resNode;
        };
        if (this.getRight()!=null){
            resNode = this.getRight().preSearch(HeroNo);
        }
        if (resNode!=null){
            return resNode;
        };
        //最后判断当前节点是不是
        if (this.getNo()==HeroNo){
            return this;
        };
        return resNode;
    }

2.3删除节点(基本) 

//删除
    public void deleteNode(int HeroNo){
        //判断左子节点
        if (this.left!=null && this.left.getNo()==HeroNo){
            this.left=null;
            return;
        }
        //判断右子节点
        if (this.right!=null&&this.right.getNo()==HeroNo){
            this.right=null;
            return;
        }
        //遍历左子树
        if (this.left!=null){
            this.left.deleteNode(HeroNo);
        }
        if(this.right!=null){
            this.right.deleteNode(HeroNo);
        }
    }

2.4二叉树 (root节点)

public class BinaryTree {
    //root
    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 preSearch(int HeroNo){
        if (this.root!=null){
            return this.root.preSearch(HeroNo);
        }else {
            return null;
        }
    }
    //中序查找
    public HeroNode infixSearch(int HeroNo){
        if (this.root!=null){
            return this.root.infixSearch(HeroNo);
        }else {
            return null;
        }
    }
    //后序查找
    public HeroNode postSearch(int HeroNo){
        if (this.root!=null){
            return this.root.postSearch(HeroNo);
        }else {
            return null;
        }
    }
    public void delNode(int HeroNo){
        if(root!=null){
            if (root.getNo()==HeroNo){
                root=null;
            }else {
                root.deleteNode(HeroNo);
            }
        }else {
            System.out.println("空树,无法删除");
        }
    }
}

2.5顺序存储二叉树  (为完全二叉树,公式)

public class ArrBinaryTree {

    private int [] arr;//存储结点的数组

    public ArrBinaryTree(int[] arr) {
        this.arr = arr;
    }
    //重载
    public void preOrder(){
        preOrder(0);
    }
    public void infixOrder(){
        infixOrder(0);
    }
    /**
     *
     * @param index  arr[index]=i
     */
    //前序
    public void preOrder(int index){
        if(arr == null ||arr.length==0){
            System.out.println("数组为空");
        }
        System.out.println(arr[index]);
        //向左递归遍历
        if ((index*2+1)<arr.length){
            preOrder(2*index+1);

        }
        //向右递归遍历
        if ((index*2+2)<arr.length){
            preOrder(2* index+2);
        }
    }
    //中序
    public void infixOrder(int index){
        if(arr == null ||arr.length==0){
            System.out.println("数组为空");
        }
        //向左递归遍历
        if ((index*2+1)<arr.length){
            infixOrder(index*2+1);
        }
        System.out.println(arr[index]);
        //向右递归遍历
        if ((index*2+2)<arr.length){
            infixOrder(2* index+2);
        }
    }
}

 2.6线索化二叉树(节点左右节点类型、前驱指针

public class ThreadBinaryTree {
    private HeroNode root;

    //为了实现线索化,需要创建要给指向当前结点的前驱结点的指针
    // 在递归进行线索化时,pre总是保留前一个结点
    //pre指针
    private HeroNode pre =null;

    public HeroNode getRoot() {
        return root;
    }

    public HeroNode getPre() {
        return pre;
    }

    public void setPre(HeroNode pre) {
        this.pre = pre;
    }

    public void setRoot(HeroNode root) {
        this.root = root;
    };
    //重载threadNodes()
    public void threadedNodes(){
        this.threadedNodes(root);
    }



    /*多回顾*/
//    //中序线索化二叉树
    public void threadedNodes(HeroNode node){
        //递归找到最左节点,然后返回
        if (node == null) {
            return;
        }
        //先线索化左子树
        threadedNodes(node.getLeft());

        //线索化当前节点
        if (node.getLeft()==null){
            node.setLeft(pre);
            node.setLeftType(1);
        }
        //key!!!
        if (pre!=null&&pre.getRight()==null){
            pre.setRight(node);
            pre.setRightType(1);
        }
        pre=node;

        //线索化右子树
        threadedNodes(node.getRight());

    };

    //***提高:中序、后序***
    //遍历线索化二叉树
    public void threadedList(){
        HeroNode node = root;
        while (root!=null){
            while(node!=null){
                //循环的找到leftType ==1的结点,第一个找到就是8结点
                // 后面随着遍历而变化,因为当leftType==1时,说明该结点是按照线索化
                // 处理后的有效结点
                while (node.getLeftType()==0){
                    node=node.getLeft();
                }
                System.out.println(node);
                while (node.getRightType()==1){
                    node=node.getRight();
                    System.out.println(node);
                }
                //替换这个遍历点(对于原本就有右结点的)!!!
                node=node.getRight();
            }
        }
    }

    //遍历二叉树
    //前序遍历
    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 preSearch(int HeroNo){
        if (this.root!=null){
            return this.root.preSearch(HeroNo);
        }else {
            return null;
        }
    }
    //中序查找
    public HeroNode infixSearch(int HeroNo){
        if (this.root!=null){
            return this.root.infixSearch(HeroNo);
        }else {
            return null;
        }
    }
    //后序查找
    public HeroNode postSearch(int HeroNo){
        if (this.root!=null){
            return this.root.postSearch(HeroNo);
        }else {
            return null;
        }
    }
    public void delNode(int HeroNo){
        if(root!=null){
            if (root.getNo()==HeroNo){
                root=null;
            }else {
                root.deleteNode(HeroNo);
            }
        }else {
            System.out.println("空树,无法删除");
        }
    }
}

线索化二叉树

 1024先发一个Blog再说

加油,励志成为一位全栈工程师->架构师的GISer_Jinger

    /*多回顾*/
    //线索化二叉树
    public void threadedNodes(HeroNode node){
        if (node == null) {
            return;
        }
        //先线索化左子树
        threadedNodes(node.getLeft());

        //线索化当前节点
        if (node.getLeft()==null){
            node.setLeft(pre);
            node.setLeftType(1);
        }

        if (pre!=null&&pre.getRight()==null){
            pre.setRight(node);
            pre.setRightType(1);
        }
        pre=node;

        //线索化右子树
        threadedNodes(node.getRight());

    };

    //遍历线索化二叉树
    public void threadedList(){
        HeroNode node = root;
        while (root!=null){
            while(node!=null){
                //循环的找到leftType ==1的结点,第一个找到就是8结点
                // 后面随着遍历而变化,因为当leftType==1时,说明该结点是按照线索化
                // 处理后的有效结点

                while (node.getLeftType()==0){
                    node=node.getLeft();
                }
                System.out.println(node);
                while (node.getRightType()==1){
                    node=node.getRight();
                    System.out.println(node);
                }
                node=node.getRight();
            }
        }
    }

 图的深度优先遍历(Depth First Search, DFS)和广度优先遍历(Breadth First Search, BFS)是图论中两种非常重要的算法,它们广泛应用于拓扑排序、道路搜索(如迷宫问题)、搜索引擎、爬虫等领域。以下是对这两种遍历算法的详细解析:

深度优先遍历(DFS)

1. 基本思想

深度优先遍历的思想是从图中某个顶点v出发,沿着一条路一直走到底,然后从这条路尽头的节点回到上一个节点,尝试另一条路,直到所有的顶点都被访问过为止。其特点是不撞南墙不回头,先尽可能深地搜索一个分支,然后再回溯搜索其他分支。

2. 实现方式

深度优先遍历主要有递归和迭代(使用栈)两种实现方式:

  • 递归实现:对于每个节点,先访问该节点,然后递归地访问其所有未被访问的邻接点。递归的优点是代码简洁,易于理解,但缺点是如果图的层次过深,可能会导致栈溢出。
  • 迭代实现:使用栈来模拟递归过程。将初始节点压入栈中,然后不断从栈中弹出节点并访问其所有未被访问的邻接点,将邻接点压入栈中。迭代实现避免了递归可能导致的栈溢出问题。

3. 应用场景

深度优先遍历常用于解决需要遍历所有可能路径的问题,如迷宫问题、图的连通性问题等。

广度优先遍历(BFS)

1. 基本思想

广度优先遍历的思想是从图中某个顶点v出发,先访问v的所有邻接点,然后再依次访问这些邻接点的邻接点,即按层次遍历图中的所有顶点。其特点是从起始点开始,一层一层地向外扩展,直到访问完所有顶点。

2. 实现方式

广度优先遍历主要使用队列来实现:

  • 初始化一个队列,将起始节点加入队列。
  • 当队列不为空时,从队列中取出一个节点并访问它。
  • 将该节点的所有未被访问的邻接点加入队列。
  • 重复上述过程,直到队列为空。

3. 应用场景

广度优先遍历常用于解决最短路径问题、层次遍历二叉树等问题。由于它按层次遍历图中的所有顶点,因此可以很容易地找到从起始点到其他所有顶点的最短路径(如果图是无向图或所有边的权重都相等的话)。

总结

深度优先遍历和广度优先遍历是图论中两种重要的遍历算法,它们各有优缺点和适用场景。深度优先遍历适用于需要遍历所有可能路径的问题,而广度优先遍历则更适用于解决最短路径等层次化问题。在实际应用中,可以根据问题的具体需求选择合适的遍历算法。

package org.example;


import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;

public class Graph {
    private ArrayList<String> vertexList;//存储顶点集合
    private int[][] edges;//存储图对应的邻接矩阵
    private int numOfEdges;//表示边的数目
    private boolean[] isVisited;//是否已被访问

    public static void main(String[] args) {
        int n = 5;
        String Vertexs[] = {"A","B","C","D","E"};
        Graph graph = new Graph(n);
        for (String vertex:Vertexs){
            graph.insertVertex(vertex);
        }
        graph.insertEdge(0,1,1);
        graph.insertEdge(0,2,1);
        graph.insertEdge(1,2,1);
        graph.insertEdge(1,4,1);
        graph.insertEdge(1,3,1);



        graph.showGraph();

//        System.out.println("===深度优先===");
//        graph.dfs();

        System.out.println("===广度优先===");
        graph.bfs();

    }

    public ArrayList<String> getVertexList() {
        return vertexList;
    }

    public void setVertexList(ArrayList<String> vertexList) {
        this.vertexList = vertexList;
    }

    public int[][] getEdges() {
        return edges;
    }

    public void setEdges(int[][] edges) {
        this.edges = edges;
    }

    public int getNumOfEdges() {
        return numOfEdges;
    }

    public void setNumOfEdges(int numOfEdges) {
        this.numOfEdges = numOfEdges;
    }

    public boolean[] getIsVisited() {
        return isVisited;
    }

    public void setIsVisited(boolean[] isVisited) {
        this.isVisited = isVisited;
    }
    //构造函数
    public Graph(int n) {
        edges = new int[n][n];
        vertexList = new ArrayList<String>(n);
        numOfEdges = 0;
    }


    /**
     *
     * @param index
     * @return 存在则返回,不存在返回-1
     */
    //获取第一个邻接节点的下标
    public int getFirstNeighbor(int index){
        for (int j = 0; j < vertexList.size(); j++) {
            if (edges[index][j]>0){
                return j;

            }
        }
        return -1;
    }

    //根据前一个邻接结点的下标来获取下一个邻接结点
    public int getNextNeighbor(int v1 , int v2){
        for (int j = v2 + 1; j < vertexList.size(); j++) {
            if (edges[v1][j]>0){
                return j;
            }
        }
        return -1;
    }

    //深度优先遍历算法

    /**
     *
     * @param isVisited
     * @param i
     */
    private void dfs(boolean[]isVisited , int i){
        //输出访问的结点
        System.out.println(getValueByIndex(i)+"->");
        //设置已访问
        isVisited[i]=true;
        int w =getFirstNeighbor(i);
        while (w!=-1){
            if (!isVisited[w]){
                dfs(isVisited,w);
            }
            //如果w结点已经被访问过
            w = getNextNeighbor(i,w);
        }
    }


    //对dfs进行一个重载,遍历我们所有结点,并进行dfs
    public void dfs(){
        isVisited = new boolean[vertexList.size()];
        //遍历所有结点,进行dfs【回溯】
        for (int i = 0; i < getNumOfVertex(); i++) {
            if (!isVisited[i]){
                dfs(isVisited,i);
            }
        }
    }



    //广度优先算法

    private void bfs(boolean[]isVisited,int i){
        int u;//表示队列的头结点对应下标
        int w;//邻接结点w
        //队列,记录结点访问的顺序
        LinkedList queue = new LinkedList();
        System.out.print(getValueByIndex(i)+"=>");
        isVisited[i]=true;
        //将结点加入队列
        queue.addLast(i);

        while (!queue.isEmpty()){
            //取出队列的头结点下标
            u=(Integer)queue.removeFirst();
            w=getFirstNeighbor(u);
            while (w!=-1){
                if (!isVisited[w]){
                    System.out.print(getValueByIndex(w)+"=>");
                    isVisited[w]=true;
                    queue.addLast(w);
                }
                w=getNextNeighbor(u,w);
            }

        }
    }

    public void bfs(){
        isVisited = new boolean[vertexList.size()];
        for (int i = 0; i < getNumOfVertex(); i++) {
            if (!isVisited[i]){
                bfs(isVisited,i);
            }
        }
    }


    //图中常用的方法
    //返回结点的个数
    public int getNumOfVertex(){
        return vertexList.size();
    }
    //显示图对应的矩阵
    public void showGraph(){
        //增强for
        for(int[] link : edges){
            System.err.println(Arrays.toString(link));
        }
    }
    //返回结点i(下标)对应的数据
    public String getValueByIndex(int i){
        return vertexList.get(i);
    }
    //返回vl和 v2的权值
    public int getWeight(int v1 ,int v2){
        return edges[v1][v2];
    }
    //插入结点
    public void insertVertex(String vertex){
        vertexList.add(vertex);
    }

    //添加边
    public void insertEdge(int v1 , int v2 , int weight){
        edges[v1][v2] = weight;
        edges[v2][v1] = weight;
        numOfEdges++;
    }


}

 递归

 1.基本概念
1.1应用场景

1.2遵守原则 

1.3常见结构 
void  backtracking(){

    if(终止条件)    {

        收集结果 

        return

    }

for(集合的元素集,类似子节点的个数)

    {

        处理结点

        递归函数;

        回溯操作

    (撤销处理结点12, 2撤销 ,13 撤销3, 14)

    }

}
2.常见应用场景 
1.迷宫问题

public class Mystery {
    public void design(){
        int[][] map = new int[8][7];
        //约定: 当 map[i][j] 为 0 表示该点没有走过 当为 1 表示墙 ; 2 表示通路可以走 ; 3 表示该点已经走过,但是走不通
        map[3][2]=1;
        map[3][1]=1;
        map[1][2]=1;
        map[2][2]=1;
        for (int i = 0; i < 7; i++) {
            map[0][i] = 1;
            map[7][i]=1;
        }
        for (int i = 0; i < 8; i++) {
            map[i][0]=1;
            map[i][6]=1;
        }
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 7; j++) {
                System.out.print(map[i][j]);
            }
            System.out.println();
        }
        System.out.println("===走迷宫后===");
        setWay(map,1,1);
        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
     */
    public boolean setWay(int[][]map,int i ,int j){
         // 在走迷宫时,需要确定一个策略(方法) 下->右->上->左 , 如果该点走不通,再回溯
        // 约定: 当 map[i][j] 为 0 表示该点没有走过 当为 1 表示墙 ; 2 表示通路可以走 ; 3 表示该点已经走过,但是走不通
        // 假定int[i][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 {
                return false;
            }

        }
    }
2.八皇后问题 
public class Queen {
    int max = 4;
    //定义数组 array, 保存皇后放置位置的结果,比如 arr = {0 , 4, 7, 5, 2, 6, 1, 3 }
    // arr[index]=val,raw=index+1,column=val+1
    int[] array = new int[max];
    static int count = 0;
    static int judgeCount = 0;

    public static void main(String[] args) {
        Queen queen = new Queen();
        queen.check(0);
        System.out.printf("一共有%d解法",count);
        System.out.printf("一个判断冲突%d次",judgeCount);
    }
    //n为第几行,需要和前几行进行检查是否符合条件
    //不同行,但也要确保不同行元素也不同列并且不在对角线上
    private void check(int n) {
        if (n == max) {
            print();
            return;
        }
        //遍历列,保证不同行元素也不在同一列或者对角线上
        for (int i = 0; i < max; i++) {
            array[n] = i;
            if (judge(n)){
                check(n+1);
            }
        }
    }

    private boolean judge(int n) {
        judgeCount++;
        //和上面的行比较判断,不能同列array[i] == array[n],不能同斜线Math.abs(array[n] - array[i]) == Math.abs(n - i)
        for (int i = 0; i < n; i++) {
            //!!!判断是否在斜线: Math.abs(array[n] - array[i]) == Math.abs(n - i)
            if (array[i] == array[n] || Math.abs(array[n] - array[i]) == Math.abs(n - i)) {
                return false;
            }
        }
        return true;
    }
    private void print(){
        count++;
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i]+" ");
        }
        System.out.println();
    }

排序算法

 排序:

插入排序:

直接插入排序

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

    public static void insertSort(int[]arr){
        int insertVal = 0;
        int insertIndex=0;
        for (int i = 1; i < arr.length; i++) {
            insertVal=arr[i];
            insertIndex=i-1;//即插入数前面的那个数
            //1. insertIndex >=0保证在给insertVal找插入位置,不越界
            // 2. insertVal < arr[insertIndex]待插入的数,还没有找到插入位置
            //实现工作:整体后移,找到合适位置
            //!!!!问题:当需要插入的数是较小的数时,后移的次数明显增多,对效率有影响.

            while (insertIndex>=0&&insertVal<arr[insertIndex]){
                arr[insertIndex+1]=arr[insertIndex];
                insertIndex--;
            }
            //这里我们判断是否需要赋值
            if (insertIndex+1!=i){
                arr[insertIndex+1]=insertVal;
            }
            System.out.println("第"+i+"轮插入");
            System.out.println(Arrays.toString(arr));
        }
    }

希尔排序

交换法
    //交换法
    public static void shellSort(int[]arr){
        int temp =0;
        int count = 0;
        for (int gap = arr.length/2; gap >0 ; gap/=2) {
             //这层循环,可用下面移动法替换,原理类似
            for (int i =gap ; i <arr.length ; i++) {
                //分组,两两一组,步长gap
                //条件j>=0,每次遍历
                for (int j = i-gap; j >=0 ; j-=gap) {
                    if (arr[j]>arr[j+gap]){
                        temp=arr[j];
                        arr[j]=arr[j+gap];
                        arr[j+gap]=temp;
                    }

                }
            }
        }
    }
移动法
//移动法
    public static  void shellSort2(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;
                    }
                    //当退出while后,就给temp找到插入的位置
                    arr[j]=temp;
                }
            }
        }
    }

交换排序

冒泡排序

public static void bubbleSort(int[]arr){
        int temp = 0;//临时变量
        boolean flag =false;//标识变量是否进行过交换
        //时间复杂都为O(n^2)
        for (int i = 0; i < arr.length-1; i++) {
            for (int j = 0; j < arr.length-1-i; j++) {
                //如果前面的数比后面的数大,则交换
                if (arr[j]>arr[j+1]){
                    flag=true;
                    temp=arr[j];
                    arr[j]=arr[j+1];
                    arr[j+1]=temp;
                }
            }
            System.out.println("第"+(i+1)+"趟排序后的数组");
            if (!flag){
                break;
            }else {
                flag=false;//重置flag!!!进行下次判断使用
            }
        }
    }

快速排序 

//chatgpt
public class FastSort {
        public static void quickSort(int[] arr) {
            if (arr == null || arr.length == 0) {
                return;
            }
            quickSort(arr, 0, arr.length - 1);
        }

        private static void quickSort(int[] arr, int low, int high) {
            if (low < high) {
                // 分区,返回基准元素的最终位置
                int pivotIndex = partition(arr, low, high);

                // 递归排序基准元素左右的两个子数组
                quickSort(arr, low, pivotIndex - 1);
                quickSort(arr, pivotIndex + 1, high);
            }
        }

        private static int partition(int[] arr, int low, int high) {
            int pivot = arr[high];
            int i = low - 1;

            // 将小于基准的元素交换到左边
            for (int j = low; j < high; j++) {
                if (arr[j] < pivot) {
                    i++;
                    swap(arr, i, j);
                }
            }

            // 将基准元素放到正确的位置
            swap(arr, i + 1, high);

            // 返回基准元素的最终位置
            return i + 1;
        }

        private static void swap(int[] arr, int i, int j) {
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }

        public static void main(String[] args) {
            int[] arr = {38, 27, 43, 3, 9, 82, 10};
            quickSort(arr);

            // 输出排序结果
            for (int num : arr) {
                System.out.print(num + " ");
            }
        }
    }
public static void quickSort(int [] arr,int left,int right){
        int l = left;
        int r = right;
        //pivot中轴值
        int pivot = arr[(left+right)/2];
        int temp = 0;//临时变量,作为交换使用
        while (l<r){
            //左侧一直找,找到大于等于pivot值才退出
            while (arr[l]<pivot){
                l+=1;
            }
            //右侧一直找,找到小于等于pivot值才退出
            while (arr[r]>pivot){
                r-=1;
            };
        //如果1>=r说明pivot的左右两的值,已经按照左边全部是l小于等于pivot值,右边全部是大于等于pivot值
            if (l>=r){
                break;
            };
            //交换
            temp=arr[l];
            arr[l]=arr[r];
            arr[r]=temp;

            //如果交换完后,发现arr[l]==pivot,r--前移
            if(arr[l]==pivot){
                r-=1;
            }
            //如果交换完后,发现arr[r]==pivot,l++后移
            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);
        }
    }

 选择排序

简单选择排序

//选择排序,从头开始选,选的元素与剩下元素比较,保证当前元素排序后小于其后所以数据
    public static void selectSort(int[]arr){
        //时间复杂都为O(n^2)
        //i<arr.length-1,与后面比较,最大可能为倒数第二
        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;
            }
            System.out.println("第"+(i+1)+"轮后~");
            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;//指向temp数组的当前索引
        //(一)
        //先把左右两边(有序)的数据按照规则填充到temp数组
        // 直到左右两边的有序序列,有一边处理完毕为止
        while (i<=mid&&j<=right){
            //如果左边的有序序列的当前元素,小于等于右边有序序列的当前元素
            // 即将左边的当前元素,填充到temp数组
            //然后 t++, i++
            if (arr[i]<=arr[j]){
                temp[t]=arr[i];
                t+=1;
                i+=1;
            }else {
                temp[t]=arr[j];
                t+=1;
                j+=1;
            }
        }

        //(二)
        //把有剩余数据的一边的数据依次全部填充到temp
        while (i<=mid){
            temp[t]=arr[i];
            t+=1;
            i+=1;
        }
        while (j<=right){
            temp[t]=arr[j];
            t+=1;
            j+=1;
        }

          //(三)
         //将temp 数组的元素拷贝到arr
        // 注意,并不是每次都拷贝所有
        t=0;
        int tempLeft = left;
        while (tempLeft<=right){
            arr[tempLeft]=temp[t];
            t+=1;
            tempLeft+=1;
        }
    }

基数排序

基数排序是桶排序的扩展

    //基数排序
    public static  void radixSort(int[]arr){
        //01 获取最大数
//        int maxNum =Arrays.stream(arr).max().getAsInt();
//        int digit = 1;
//        //获取最大数位数
//        while (maxNum/Math.pow(10,digit)>0){
//            digit++;
//        }

        //02获取在最大数
        int max = arr[0];//假设为第一个数
        for (int i = 1; i <arr.length ; i++) {
            if (arr[i]>max){
                max=arr[i];
            }
        }
        //获取最大数长度
        int maxLength =(max+"").length();
        //定义一个二维数组,表示10个桶,每个桶就是一个一维数组
        // 说明
        //1.二维数组包含10个一维数组
        // 2.为了防止在放入数的时候,数据溢出,则每个一维数组(桶),大小定为arr.1ength
        // 3.明确,基数排序是使用空间换时间的经典算法

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

        //为了记录每个桶中,实际存放了多少个数据,我们定义一个一维数组来记录各个桶的每次放入的数据个数
        // 可以这里理解
        //比如: bucketElementCounts[0],记录的就是bucket[0]桶的放入数据个数
        
        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;//key1
                //放入对应桶中
                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
                        arr[index++]=bucket[k][l];
                    }
                }
                //第i+1处理后,休要将每个bucketElementCounts置为0
                bucketElementCounts[k]=0;
            }
            System.out.println("第"+(i+1)+"轮,排序处理后arr="+Arrays.toString(arr));
        }
    }

查找算法

 二分🔍

//二分查找
    //前提:先需进行排序
    //关键:递归查找
    //递归退出条件:1.找到2.递归完整个数组,仍然没有找到,也需结束递归,条件:left>right
    public static List<Integer> binarySearch(int[]arr, int left, int right, int findVal){
        //递归整个数组,没找到
        //条件left>right
        if (left>right){
            return new ArrayList<Integer>();
        }
        int mid =(left+right)/2;
        int midVal =arr[mid];
        if (findVal>midVal){
            return binarySearch(arr,mid+1,right,findVal);
        } else if (findVal<midVal) {
            return binarySearch(arr,left,mid-1,findVal);
        }else {
             //*思路分析
             //*1.在找到mid索引值,不要马上返回
             //*2.向mid索引值的左边扫描,将所有满足值,的元素的下标,加入到集合ArrayList
             //*3.向mid索引值的右边扫描,将所有满足值,的元素的下标,加入到集合ArrayList
            //*4.将Arraylist返回
            List<Integer> resIndexlist = new ArrayList<Integer>();

            //向左扫描
            int temp = mid-1;
            while (true){
                if (temp<0||arr[temp]!=findVal){
                    break;//退出
                }
                //否则就将temp放入到resIndexlist
                resIndexlist.add(temp);
                temp-=1;//temp左移
            }
            resIndexlist.add(mid);

            //向右扫描
            temp=mid+1;
            while (true){
                if (temp>arr.length-1||arr[temp]!=findVal){
                    break;
                }
                //否则就将temp放入到resIndexlist
                resIndexlist.add(temp);
                temp+=1;
            }
            return resIndexlist;
        }
    }

 插值🔍

key:

Low+(High-Low)*(findVal-arr[Low])/(arr[High]-arr[Low])//其中arr[Low]<=findVal<=arr[High]
//插值查找
    //前提:先需进行排序
    //关键1:递归查找
    //关键2:mid自适应求解算法:Low+(High-Low)*(findVal-arr[Low])/(arr[High]-arr[Low])//其中arr[Low]<=findVal<=arr[High]
    //递归退出条件:1.找到2.递归完整个数组,仍然没有找到,也需结束递归,条件:left>right
    public static List<Integer> InterpSearch(int[]arr, int left, int right, int findVal){
        //递归整个数组,没找到
        if (left>right||findVal<arr[0]||findVal>arr[arr.length-1]){
            return new ArrayList<Integer>();
        }
        int mid =left+(right-left)*(findVal-arr[left])/(arr[right]-arr[left]);
        int midVal =arr[mid];
        if (findVal>midVal){
            return InterpSearch(arr,mid+1,right,findVal);
        } else if (findVal<midVal) {
            return InterpSearch(arr,left,mid-1,findVal);
        }else {
            //*思路分析
            //*1.在找到mid索引值,不要马上返回
            //*2.向mid索引值的左边扫描,将所有满足值,的元素的下标,加入到集合ArrayList
            //*3.向mid索引值的右边扫描,将所有满足值,的元素的下标,加入到集合ArrayList
            //*4.将Arraylist返回
            List<Integer> resIndexlist = new ArrayList<Integer>();

            //向左扫描
            int temp = mid-1;
            while (true){
                if (temp<0||arr[temp]!=findVal){
                    break;//退出
                }
                //否则就将temp放入到resIndexlist
                resIndexlist.add(temp);
                temp-=1;//temp左移
            }
            resIndexlist.add(mid);

            //向右扫描
            temp=mid+1;
            while (true){
                if (temp>arr.length-1||arr[temp]!=findVal){
                    break;
                }
                //否则就将temp放入到resIndexlist
                resIndexlist.add(temp);
                temp+=1;
            }
            return resIndexlist;
        }
    }

 斐波那契查找算法

public class FibonacciSearch {
    //因为后面我们mid=low+F(k-1)-1,需要使用到斐波那契数列,因此我们需要先获取到一个斐波那契数列
    // 非递归方法得到一个斐波那契数列
    public static int[] fib(int maxSize){
        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;//存放mid值
        int f[]=fib(20);

        //获取到斐波那契分割数值的下标
        while (high>f[k]-1){
            k++;
        }
        //因为f[k]值可能大于a的长度,因此我们需要使用Arrays类,构造一个新的数组,并指向temp[]
        //不足的部分会使用0填充
        int[] temp = Arrays.copyOf(a,f[k]);

        //实际上需求使用a数组最后的数填充temp
        // 举例:
       //temp= {1,8,10,89,1000,1234,0,0}=>{1,8,10,89,1000,1234,1234,1234,}
        for (int i = high+1; i <temp.length ; i++) {
            temp[i]=a[high];
        }

        //使用while循环找到key
        while (low<=high){
            mid=low+f[k-1]-1;
            if (key<temp[mid]){
                //继续向左边查找
                high=mid-1;
                //为甚是k--
                // 说明
                 // 1.全部元素=前面的元素+后边元素
                // 2.f[k]= f[k-1]+ f[k-2]
                //因为前面有fk-1]个元素,所以可以继续拆分f[k-1]=f[k-2]+ f[k-3]
                //即在f[k-1]的前面继续查找k--
                // 即下次循环mid= f[k-1-1]-1
                k--;
            } else if (key>temp[mid]) {
                //向右边继续查找
                low=mid+1;
                //*为什么是k-2

                //说明
                //1.全部元素=前面的元素+后边元素
                // 2.f[k]=f[k-1]+ f[k-2]
                //3.因为后面我们有f[k-2]所以可以继续拆分f[k-2]= f[k-3]+ f[k-4]
                // 4.即在f[k-2]的前面进行查找k -=2
               //5.即下次循环mid= f[k-1-2]- 1
                k-=2;
            }else {
                //找到
                if (mid<=high){
                    return mid;
                }else {
                    return high;
                }
            }
        }
        return -1;
    }

分治算法

 定义:

分治法是一种很重要的算法。字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。这个技巧是很多高效算法的基础,如排序算法(快速排序,归并排序),傅立叶变换(快速傅立叶变换)

基本步骤 

1) 分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题
2) 解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题                              3) 合并:将各个子问题的解合并为原问题的解。

应用:

1. 汉诺塔问题

    public static void hanoiTower(int num ,char a,char b,char c){
        //如果只有一个盘
        if(num == 1){
            System.out.println("第一个从 " + a + " 移动到 " + c);
        }else {
            //如果num>=2,总是可以看成两个盘1.最下边的一个盘2.上面所有盘
            //1.把最上边的所有盘A,移动到B,使用C做辅助
            hanoiTower(num - 1, a, c, b);
            //2.把最下边的盘A,移动到C
            System.out.println("第 " + num + " 个从 " + a + " 移动到 " + c);
            //3.把B所有的盘,移动到C,使用A做辅助
            hanoiTower(num - 1, b, a, c);
        }

    }

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2079650.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在 AMD GPUs 上进行图分析使用 Gunrock

Graph analytics on AMD GPUs using Gunrock — ROCm Blogs 图和图分析是可以帮助我们理解复杂数据和关系的相关概念。在这种背景下&#xff0c;图是一种数学模型&#xff0c;用于表示实体&#xff08;称为节点或顶点&#xff09;及其连接&#xff08;称为边或链接&#xff09;…

【CTF Web】BUUCTF BUU BRUTE 1 Writeup(弱口令+暴力破解+字典攻击)

BUU BRUTE 1 1 点击启动靶机。 解法 随便输个用户名。 试试 admin。 用 burp 抓包。 生成四位数字的字典。 导入字典到 burp。 添加载荷位置。 开始爆破。破解完成&#xff0c;密码&#xff1a;6490。取得 flag。 注意 如果破解得慢的话&#xff0c;记得要续期靶机。不然靶机…

算法工程师秋招面试问题总结

大模型分布式训练并行 一般有 tensor parallelism、pipeline parallelism、data parallelism 几种并行方式,分别在模型的层内、模型的层间、训练数据三个维度上对 GPU 进行划分。三个并行度乘起来,就是这个训练任务总的 GPU 数量。 1.数据并行 数据并行是最常见的并行形式…

2024.8.27 作业

1> 提示并输入一个字符串&#xff0c;统计该字符串中字母个数、数字个数、空格个数、其他字符的个数 #include <iostream>using namespace std;int main() {string s;cout << "请输入字符串>>>";getline(cin,s);int letter0,digit0,blank0,…

git 复制提交到另外分支上

查看提交id 在原分支上查看要复制的id git log切换目标分支 将刚才复制的id&#xff0c;在这个目标分支上执行复制命令 git cherry-pick <commit-id>其中是要复制的提交的提交ID 效果 新分支上未复制的提交&#xff1a; 新分支上已复制的提交&#xff1a;

PTA - C语言国庆题集2

目录 7-21 打妖怪7-22 统计连续高温的最大天数7-23 唱歌比赛打分7-24 找最长的字符串7-25 算龙脉7-26 DNA鉴定7-28 T9键盘7-31 单链表的创建&#xff0c;遍历与销毁7-36 有多少位是7&#xff1f;7-37 选择排序7-38 翻转单词顺序7-39 求因子和最大的数&#xff08;结构体排序&am…

min(n,k)*min(m,k)

今天看那场一题都没写出来的div12&#xff0c;发现我想了那么久的A题&#xff0c;别人用几行代码就搞出来了。。。&#xff0c;现在感觉这道题和状压dp好像。。。 这道题用到了切比雪夫距离。。。

2024114读书笔记|《漱玉词》—— 绛绡缕薄冰肌莹,雪腻酥香,满院东风,海棠铺绣,梨花飞雪

2024114读书笔记|《漱玉词》—— 绛绡缕薄冰肌莹&#xff0c;雪腻酥香&#xff0c;满院东风&#xff0c;海棠铺绣&#xff0c;梨花飞雪 《漱玉词》李清照&#xff0c;观之入微&#xff0c;是惆怅亦是欢乐&#xff0c;不费力就可以读完的小诗词&#xff0c;插图不错。 知否的主…

Spring理论知识(Ⅱ)——Spring核心容器模块

Spring的组成 Spring由20个核心依赖组成&#xff0c;这20个核心依赖可以分为6个核心模块 本篇文章着重描述Spring核心容器模块&#xff0c;其中包含了spring-beans&#xff0c;spring-core&#xff0c;spring-context&#xff0c;spring-expression-language&#xff08;…

摄像头设备问题如何检测

摄像头等智能设备的在线状态通常被视为其运作正常的表现。但在日常监控使用中&#xff0c;由于使用空间、网络环境、产品年限等原因&#xff0c;设备掉线、视频流无法正常获取、监控画面异常&#xff08;如花屏&#xff09;&#xff0c;以及存储介质&#xff08;如SD卡&#xf…

【Electron】Electron学习笔记

1.什么是 Electron&#xff1f; Electron 是一个跨平台桌面应用开发框架&#xff0c;开发者可以利用 HTML、CSS、JavaScript 等Web技术来构建桌面应用程序。它本质上是结合了 Chromium 和 Node.js&#xff0c;目前广泛用于桌面应用程序开发。例如&#xff0c;许多桌面应用都采…

算法学习-基础算法

基础算法 一.二分查找 1.模版 boolean check(int x) { }int search(int left, int right) {while (left < right) {int mid (left right) >> 1;if (check(mid)) {//满足条件&#xff0c;向寻找范围继续寻找&#xff0c;例如我要找更靠左的&#xff1a;r m right…

一次学校OJ 代码执行测试

前言 以前看过一篇Windows上搭OJ被C#打穿的文章&#xff0c;刚好测测学校的OJ。 这里没有过多的研究其余的可能利用点&#xff0c;仅仅是简单记录下过程&#xff0c;一些思路的启发。 测试过程 首先看支持的代码类型&#xff1a; 尝试了Java发现不能import&#xff0c;那J…

一文带你从零到实战,学会gcc和Makefile,多文件编译神器的使用与编写

目录&#xff1a; 目录&#xff1a; 一、什么是Makefile 1.1 makefile的作用&#xff1a; 1.2 makefile的基本组成&#xff1a; 二、Linux编译过程&#xff1a; 2.1 linux编译过程: 2.1.1 预处理&#xff08;Preprocessing&#xff09; 2.1.2 编译&#xff08;Compilation&am…

# 移动硬盘误操作制作为启动盘数据恢复问题

移动硬盘误操作制作为启动盘数据恢复问题 文章目录 移动硬盘误操作制作为启动盘数据恢复问题步骤一恢复原有数据 步骤二格式化并重新分区 注意注意先找数据恢复软件恢复数据&#xff0c;把之前移动硬盘或者U盘上的数据恢复到其它地址 步骤一 恢复原有数据 使用一些数据恢复软…

SpringBoot实现Word转PDF/TXT

背景 研发工作中难免会遇到一些奇奇怪怪的需求&#xff0c;就比如最近&#xff0c;客户提了个新需求&#xff1a;上传一个WORD文档&#xff0c;要求通过系统把该文档转换成PDF和TXT。客户的需求是没得商量的&#xff0c;必须实现&#xff01;承载着客户的期望&#xff0c;我开始…

培训第三十七天(Dockerfile与registry)

一、使用Dockerfile创建镜像 Dockerfile文件命令介绍&#xff1a; FORM 指定基础镜像为该镜像的最后修改版本 FROM < img:tag >指定基础镜像为该镜像的⼀个tag版本 MAINTAINER 指定镜像创建者&#xff0c;企业内部不⽤指定&#xff0c;对外发布也可以不指定 RUN 运⾏…

探索Python的Excel力量:openpyxl库的奥秘

文章目录 探索Python的Excel力量&#xff1a;openpyxl库的奥秘背景&#xff1a;为什么选择openpyxl&#xff1f;库简介&#xff1a;openpyxl是什么&#xff1f;安装指南&#xff1a;如何安装openpyxl&#xff1f;快速上手&#xff1a;五个基本函数实战演练&#xff1a;三个应用…

Python实现Word文档转换为图片(JPG、PNG、SVG等常见格式)例子解析

在Python中将Word文档转换为图片&#xff08;如JPG、PNG、SVG等格式&#xff09;可以通过多种库实现&#xff0c;例如Spire.Doc for Python和Aspose.Words for Python。以下是一些详细的代码示例&#xff0c;展示了如何使用这些库完成转换。 使用Spire.Doc for Python转换Word…

网络服务器及IO模型

网络服务器 单循环服务器&#xff1a;服务器在同一时刻只能响应一个客户端的请求 并发服务器模型&#xff1a;服务器在同一时刻可以响应多个客户端的请求 实现TCP并发服务器 1.多进程 2.多线程 3.IO多路复用&#xff1a; 为了解决进程或线程阻塞到某个 I/O 系统调用而出现的…