数据结构与算法(1)

数据结构与算法(1)

算法笔记

学习链接尚硅谷

https://www.bilibili.com/video/BV1E4411H73v?p=10

一、稀疏数组

稀疏数组规则:

  •  第一行:旧数组的行数 旧数组的列数 旧数组的非零数、
    
  •  第二行:非零数所在行数 非零数所在列数 非零数值
    
  •  。(以此类推)
    

image-20210724125344707

1.1转为稀疏数组

/**
 * 转为稀疏数组
 */
public static int[][] toSparseArray(int[][] array){
    int sum = showArray(array);
    int [][]sparseArray = new int[sum+1][3];
    //为稀疏数组赋值
    sparseArray[0][0] = 7;
    sparseArray[0][1] = 7;
    sparseArray[0][2] = sum;
    int count = 0;
    for (int i = 0; i < array.length; i++){
        for (int j = 0; j < array[i].length; j++){
            if(array[i][j] != 0){
                count++;
                //赋值
                sparseArray[count][0] = i;
                sparseArray[count][1] = j;
                sparseArray[count][2] = array[i][j];

            }
        }
    }
    return sparseArray;
}

1.2稀疏数组转为正常数组

/**
 * 稀疏数组转为正常数组
 */
public static int[][] sparseArrayToArray(int[][] sparseArray){
    int array[][] = new int[sparseArray[0][0]][sparseArray[0][1]];
    for(int i = 1; i< sparseArray.length; i++){
            array[sparseArray[i][0]][sparseArray[i][1]] = sparseArray[i][2];
    }
    return array;
}

二、单链表

2.1添加节点构造方法

/**
 * 添加节点考虑序号
 * @param node
 */
public void addNodeByNum(Node node){
    //获取节点数据内的no值
    int no = node.getNo();
    //遍历
    //当前节点的no值小于添加节点的no值时,进行添加
    Node temp = head;
    //循环用于寻找添加节点的位置
    /*
    三种情况可以找到节点位置,找到后退出循环
    1.当前节点为最后一个节点
    2.当前节点的下一个节点的no值大于新的node的no值
    3.如果编号存在,则添加失败,也退出循环
     */
    //判断新节点的no值是否存在,默认不存在
    boolean isExist = false;
    while(true){
        //temp为最后一个节点
        if(temp.next == null){
            break;
        }
        //当前节点的下一个节点的no值大于新的node的no值
        else if (temp.next.getNo() > node.getNo()){
            break;
        }
        //已经存在编号
        else if(temp.next.getNo() == node.getNo()){
            //标志改为存在
            isExist = true;
            break;
        }
        //后移
        temp = temp.next;
    }
    //进行添加操作(2为新节点)
    if(isExist){
        System.out.println("编号已存在,不可添加");
        return;
    }
    //2连3
    node.next = temp.next;
    //1连2
    temp.next = node;
    System.out.println(node.getNo()+"号,添加成功");
}

主函数

public class SingleList {
    public static void main(String[] args) {
        Node node1 = new Node(1,"小王",20);
        Node node3 = new Node(3,"小张",13);
        Node node2 = new Node(2,"小李",15);

        SingleListt singleListt = new SingleListt();
        //singleListt.addNode(node1);
        //singleListt.addNode(node3);
        //singleListt.addNode(node2);
        singleListt.addNodeByNum(node1);
        singleListt.addNodeByNum(node3);
        singleListt.addNodeByNum(node2);
        singleListt.addNodeByNum(node2);

        singleListt.traverseList();
    }

}

image-20210725112840379

2.2练习题

1.计算单链表的有效节点数

/**
 * 计算链表的有效节点数
 */
public int numberOfComputingNodes(){
    int count = 0;
    if(head.next == null){
        //空链表
        return 0;
    }
    while (head.next != null){
        count++;
        head = head.next;
    }
    return count;
}

2.查找单链表的倒数第k个节点

/**
 * 查找单链表的倒数第k个节点
 */
public Node findDecK(int k){
    Node temp = head.next;
    //链表为空
    if(temp == null){
        return null;
    }
    //计算链表的有效节点数
    int count =  numberOfComputingNodes();
    System.out.println("有效节点数:"+count);

    for (int i = 0; i < count - k; i++){
        //节点移动count-k次
        temp = temp.next;
    }
    return temp;
}

3.反转单链表

/**
 * 反转单链表(创建新的头结点,将原链表的节点取出,并使用头插法插入到新的头结点,最后将原链表的头结点连接第一个节点)
 *
 * 步骤1.创建新的头结点
 *     2。将原链表的节点取出,并使用头插法插入到新的头结点
 *     3.最后将原链表的头结点连接第一个节点
 */
public void reverseLinkedList(){
    //空链表或一个节点的链表,不反转
    if(head.next == null || head.next.next == null){
        return;
    }
    //1.创建新的头结点
    Node tempHeadNode = new Node();
    Node temp = head.next;
    //遍历到尾节点
    //2。将原链表的节点取出,并使用头插法插入到新的头结点
    while (temp != null){
        //用于保存原链表中temp的下一节点
        Node next = temp.next;
        temp.next = tempHeadNode.next;
        tempHeadNode.next = temp;

        //后移
        temp = next;
    }
    //3.最后将原链表的头结点连接第一个节点
    head.next = tempHeadNode.next;
}

4.合并两个有序链表

/**
 * 合并两个有序链表,合并完仍然有序
 * 步骤1.创建一个新的头结点,指向一个有序链表
 *  2.遍历第一、二个有序链表链表,对每个节点进行插入
 *      2.1找到合适插入位置的前一个,进行插入
 */
public Node mergeLinkedList(SingleListt singleList){
    //1.创建一个新的头结点,指向一个有序链表

    Node newHead = head;
    newHead.next = head.next;
    //2.遍历第二个有序链表链表,对每个节点进行插入
    Node temp = singleList.getHead().next;
    Node newHeadTemp = newHead;
    //当第二个链表为空时,直接返回新节点
    if(temp == null){
        System.out.println("要合并的链表为空");
        return head;
    }
    //第二个链表遍历到尾节点
    while(temp != null){
        Node next = temp.next;
        //2.1找到合适插入位置的前一个,进行插入
        while(newHeadTemp.next!=null){
            if (temp.getNo() <= newHeadTemp.next.getNo()){
                break;
            }
            newHeadTemp = newHeadTemp.next;
        }
        //找到后插入
        temp.next = newHeadTemp.next;
        newHeadTemp.next = temp;
        //后移
        temp = next;
    }

    return newHead;
}

5.约瑟夫问题

约瑟夫问题是个有名的问题:N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。例如N=6,M=5,被杀掉的顺序是:5,4,6,2,3。

小孩类

/**
 * 小孩类
 */
class Boy{
    /**
     * 编号
     */
    private int no;
    /**
     * 指向下一节点
     */
    private Boy next;

    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;
    }

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

循环单项链表类

/**
 * 单向循环链表类
 */
class SingleCircleLinkList{
    /**
     * 相当于头结点
     */
    private Boy first;

    public Boy getFirst() {
        return first;
    }

    /**
     * 添加男孩
     * @param nums 数量
     */
    public void addBoy(int nums){
        //nums值校验
        if(nums < 1){
            System.out.println("人数不正确");
            return;
        }
        //先创建第一个boy,first指向第一个boy
        Boy boy = new Boy(1);
        first = boy;
        //第一个boy的next指向自己
        boy.setNext(first);
        Boy temp = first;
        //实例化boy
        for (int i = 2; i <= nums; i++) {
            Boy newBoy = new Boy(i);
            //连接当前boy和newBoy
            temp.setNext(newBoy);
            //连接newBoy和first,形成环状
            newBoy.setNext(first);
            //后移
            temp = temp.getNext();
        }
    }
    /**
     * 遍历
     */
    public int getBoys(){
        int num = 0;
        if(first == null){
            return 0;
        }
        Boy temp = first;
        while (true){
            int no = temp.getNo();
            num++;
            //到达末尾
            if(temp.getNext() == first){
                break;
            }
            temp = temp.getNext();
        }
        return num;
    }
}

约瑟夫主类

public class Josephu {
    public static void main(String[] args) {
        //创建链表
        SingleCircleLinkList singleCircleLinkList = new SingleCircleLinkList();
        singleCircleLinkList.addBoy(6);
        Josephu(singleCircleLinkList,5,6);
    }
    public  static void Josephu(SingleCircleLinkList list,int m, int nums){
        int step = m - 1;
        Boy first = list.getFirst();
        //先找到尾节点
        Boy tailNode = getTailNode(list);
        Boy temp = tailNode;

        while (true){
            for (int i = 0; i < step; i++) {
                temp = temp.getNext();
            }
            //first永远在temp的后一个,可有可无
            first = temp.getNext();
            //删除first所在节点
            System.out.println(first.getNo());
            temp.setNext(first.getNext());
            nums--;
            if(nums == 0){
                break;
            }
        }
    }
    /**
     * 获取尾节点
     */
    public static Boy getTailNode(SingleCircleLinkList list){
        int nums = list.getBoys();
        Boy first = list.getFirst();
        Boy temp = first;
        while (true){
            //尾节点
            if(temp.getNext() == first){
                return temp;
            }
            temp= temp.getNext();
        }
    }
}

三、栈

单链表实现栈

public class LinkList {
    /**
     * 链表的头结点
     */
    private Node head = new Node(0,null);
    private int max;

    public LinkList(Node head) {
        this.head = head;
    }

    public LinkList() {
    }
    public LinkList(int max) {
        this.max = max;
    }

    public Node getHead() {
        return head;
    }

    public void setHead(Node head) {
        this.head = head;
    }
    /**
     * 判断栈内是否已满
     */
    public boolean isFull(){
        boolean isFull = false;
        //计算栈内元素数
        int num = 0;
        Node temp = head;
        while (true){
            if(num == max){
                isFull = true;
            }
            if(temp.getNext() == null){
                //尾节点了
                break;
            }
            temp = temp.getNext();
            num++;
        }
        return isFull;
    }
    /**
     * 入栈操作
     */
    public void push(int value){
        //判断是否已满
        if (isFull()){
            System.out.println("栈内已满");
            return;
        }
        Node newNode = new Node(value);
        Node temp = head;
        while(true){
            if (temp.getNext() == null){
                temp.setNext(newNode);
                break;
            }
            temp= temp.getNext();
        }

    }
    /**
     * 判断是否为空
     */
    public boolean isEmpty() {
        return head.getNext() == null;
    }
    /**
     * 出栈操作
     */
    public int pop(){
        int ele;
        //判空
        if(head.getNext() == null){
            System.out.println("栈为空,不可出栈");
            return 0;
        }
        Node temp = head;
        while (true){
            if(temp.getNext().getNext() == null){
                ele = temp.getNext().getNo();
                //出栈
                temp.setNext(null);
                break;
            }
            temp=temp.getNext();
        }
        return ele;
    }
    /**
     * 查看栈顶元素
     * @return
     */
    public int look(){
        int ele;
        //判空
        if(head.getNext() == null){
            return '0';
        }
        Node temp = head;
        while (true){
            if(temp.getNext() == null){
                ele=  temp.getNo();
                break;
            }
            temp=temp.getNext();
        }
        return ele;
    }
    /**
     * 展示栈内元素
     */
    public void showElement(){
        //判空
        if(head.getNext() == null){
            System.out.println("栈内空");
            return;
        }
        Node temp = head.getNext();
        while (true){
            //尾节点
            if(temp.getNext() == null){
                System.out.println(temp.getNo());
                break;
            }
            System.out.println(temp.getNo());
            temp= temp.getNext();
        }
    }

}

节点类

public class Node {
    /**
     * 编号
     */
    private int no;
    /**
     * 指针域
     */
    private Node next;


    public Node() {
    }

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

    public int getNo() {
        return no;
    }

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

    public Node getNext() {
        return next;
    }

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

3.1栈实现简单计算器

步骤

  1. 分类数栈和操作数栈,并根据元素分别进入对应类别栈
  2. 判断加入操作数占是否为空
    1. 空,直接加入操作数
    2. 不为空,判断优先级
      1. 当要加入的操作数优先级<=操作数栈顶优先级时,取出两个数栈元素和操作数栈顶元素计算,再将新的操作数加入栈
      2. 小于则直接入栈
  3. 加入数栈时要判断下一位是否为数栈,为数栈则扩展字符串,知道出现操作数为止。(用完字符串要置空)
  4. 全部入栈后,继续计算(取出两个数栈元素,一个操作数)
  5. 当操作数栈为空时,计算完毕,将最后计算的结果加入数栈(为最终答案)
public class Stack {
    public static void main(String[] args) {
        LinkList OperLinkList = new LinkList(5);
        LinkList NumLinkList = new LinkList(5);
        String temp ="";
        String str = "100+2*2";
        char[] chars = str.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            //判断是否为操作符
            if(isOper(chars[i])){
                if (OperLinkList.isEmpty()){
                    //操作符栈为空
                    //入栈
                    OperLinkList.push(chars[i]);
                }
                else{
                    if(OperPriority(chars[i]) <= OperPriority( OperLinkList.look())){
                        //小于等于栈内的优先级
                        //取出两个数栈元素和一个操作栈元素,进行计算
                        int res = calculate(NumLinkList.pop(),NumLinkList.pop(),OperLinkList.pop());
                        //计算结果入数栈
                        NumLinkList.push(res);
                        OperLinkList.push(chars[i]);
                    }
                    else {
                        //入栈
                        OperLinkList.push(chars[i]);
                    }
                }
            }
            else {
                //进数栈(可能不是个位数,需要拼接)
                //NumLinkList.push(chars[i]-48);
                    temp+=chars[i];
                    //判断是否为最后一位
                    if(i == chars.length-1){
                        //直接入栈
                        NumLinkList.push(Integer.parseInt(temp) );
                    }
                    else {
                        //一直添加到有操作符为止
                        if(isOper(chars[i+1])){
                            //入栈
                            NumLinkList.push(Integer.parseInt(temp));
                            //清空temp缓存
                            temp="";
                        }
                    }
            }

        }
        //全部入栈完毕
        //依次取出数栈和操作数栈进行计算
        while (true){
            if (OperLinkList.isEmpty()){
                break;
            }
            int res = calculate(NumLinkList.pop(),NumLinkList.pop(),OperLinkList.pop());
            NumLinkList.push(res);
        }
        System.out.println("结果为:"+ NumLinkList.pop());

    }

    /**
     * 判断是否为操作符
     * @param oper
     * @return
     */
    public static boolean isOper(int oper){
        return oper =='+' || oper =='-'|| oper=='*'||oper=='/';
    }
    /**
     * 判断操作符的优先级
     */
    public static int OperPriority(int oper){
        if(oper == '+' ||oper == '-'){
            return 0;
        }
        else if(oper == '*' || oper == '/'){
            return 1;
        }
        else {
            //出错
            return -1;
        }
    }
    /**
     * 计算
     * @return
     */
    public static int calculate(int num1, int num2, int oper){
        int res = 0;
        switch (oper){
            case '+':
                res = num2 + num1;
                break;
            case '-':
                res = num2 - num1;
                break;
            case '*':
                res = num2 * num1;
                break;
            case '/':
                res = num2 / num1;
                break;
            default:
                System.out.println("符号错误");
                break;

        }

        return res;
    }
}

3.2逆波兰表达式

public class PostfixExpression {
    /**
     * 后缀表达式计算器
     *
     * @param args
     */
    public static void main(String[] args) {
        //(3+4)*5-6 -->29
        String postfixExpression = "3 4 + 5 * 6 -";
        //1.将字符串放入数组中
        String[] strings = postfixExpression.split(" ");
        //2.将数组内元素依次入栈
        Stack<String> stack = new Stack<>();
        for (String string : strings) {
            //如果是数,则入栈(用正则表达式考虑多位数)
            if (string.matches("\\d+")) {
                //为数
                //入栈
                stack.push(string);
            } else {
                //操作符,则pop出两个栈顶元素,并计算,后算前
                String s1 = stack.pop();
                String s2 = stack.pop();
                String res = calculate(s1, s2, string);
                //将计算结果入栈
                stack.push(res);
            }
        }
        //最后栈内元素为最终结果
        int calRes = Integer.parseInt(stack.pop());
        System.out.println("结果为:" + calRes);

    }

    /**
     * 计算两个出栈的数
     *
     * @param s1   先出栈元素
     * @param s2   后出栈元素
     * @param oper 操作符
     * @return 计算结果,String类型
     */
    public static String calculate(String s1, String s2, String oper) {
        int num1 = Integer.parseInt(s1);
        int num2 = Integer.parseInt(s2);
        int res = 0;
        switch (oper) {
            case "+":
                res = num2 + num1;
                break;
            case "-":
                res = num2 - num1;
                break;
            case "*":
                res = num2 * num1;
                break;
            case "/":
                res = num2 / num1;
                break;
            default:
                throw new RuntimeException("符号出错");
        }

        return "" + res;
    }
}

四、递归

4.1递归实现迷宫问题

public class RecursionDemo {
    /**
     * 递归实现迷宫问题
     * @param args
     */
    public static void main(String[] args) {
        //创建迷宫地图
        /**
         * 迷宫 1:代表为墙
         *     2:代表为通路
         *     3:代表为走过的死路
         *     0:没走过的路
         */
        int map[][] = new int[8][7];
        //设置墙壁
        for (int i = 0; i < 7; i++) {
            map[7][i] = 1;
            map[0][i] = 1;
        }
        for (int i = 1; i < 8; i++) {
            map[i][0] = 1;
            map[i][6] = 1;
        }
        map[3][1] = 1;
        map[3][2] = 1;
        map[6][5] = 4;

        findWay(map,1,1);
        for (int[] ints : map) {
            for (int anInt : ints) {
                System.out.printf(anInt+" ");
            }
            System.out.println("");
        }

    }

    /**
     * 寻找出口
     * @param map 迷宫地图
     * @param i 初始横坐标
     * @param j 初始纵坐标
     * @return
     */
    public static boolean findWay(int [][]map, int i, int j){
        //判断是否到出口
        if(map[5][4] == 2){
            return true;
        }
        else {
            if (map[i][j] == 0){
                //把当前位置假设为通路
                map[i][j] =2;
                //向下寻找
                if (findWay(map,i+1,j)) {
                    return true;
                }
                //向右
                else if(findWay(map,i,j+1)){
                    return true;
                }
                //向上
                else if(findWay(map,i-1,j)){
                    return true;
                }
                //向左
                else if(findWay(map,i,j-1)){
                    return true;
                }
                else {
                    map[i][j] = 3;
                    return false;
                }
            }
            else {
                return false;
            }
        }
    }
}

image-20210728125428673

4.2递归实现八皇后问题

public class EightQueens {
    /**
     * 递归实现八皇后问题
     * 保证八个旗子互相不能在同一行、列、对角线(8*8的棋盘)
     */
    final static int maxCount = 8;
    static int[] solution = new int[maxCount];
    //最多8枚棋子

    static int count = 0;

    public static void main(String[] args) {
        //数组代表一次可行策略,下标n代表第n个棋子,值row代表第row列
        //solution[n] = row;
        put(0);
        System.out.println(count);

    }
    public static void put(int n){
        //已经放完8枚了
        if(n == maxCount){
            showSolution();
            return;
        }
        else {
            for (int i = 0; i < maxCount; i++){
                solution[n] =i;
                if (!ifJudgmentConflict(n)){
                    put(n+1);
                }
            }
        }

    }

    /**
     * 判断第n个棋子和之前的所有棋子是否冲突
     * @return
     */
    public static boolean ifJudgmentConflict(int n){
        //判断第n个棋子和其他棋子冲突
        for (int i = 0; i < n; i++) {
            //是否在同一列或是否在对角线(纵坐标等于横坐标)
            if(solution[n] == solution[i] || Math.abs(n-i) == Math.abs(solution[n]-solution[i])){
                //冲突
                return true;
            }
        }
        return false;
    }

    /**
     * 遍历数组
     */
    public static void showSolution(){
        count++;
        for (int i : solution) {
            System.out.printf(i+" ");
        }
        System.out.println("");
    }
}

五、排序

度量程序执行时间的两种方法

  1. 事后统计方法:要求同一台机器同一状态
  2. 事前估算方法: 分析程序的时间复杂度

排序

image-20210728154504713

image-20210728155629852

image-20210731135624763

5.1冒泡排序

每次寻找一个最大的数放在最后

优化:当本次循环没有交换,则完成排序,不继续比较

public class BubbleSort {
    public static void main(String[] args) {
        int []arr = {12,43,5234,1,54,14};
        bubbleSort(arr);
    }

    /**
     * 冒泡
     * @param arr
     */
    public static void bubbleSort(int []arr){

        //判断本次循环是否有交换(优化)
        boolean ifExchange = false;
        int temp = 0;
        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]){
                    //交换
                    ifExchange = true;
                    temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
            if (ifExchange){
                //复原用于下一次循环
                ifExchange = false;
            }
            else {
                break;
            }
            printArr(arr);

        }
    }
    /**
     * 打印数组
     */
    public static void printArr(int []arr){
        for (int i : arr) {
            System.out.printf(i+" ");
        }
        System.out.println();
    }
}

5.2选择排序

每趟循环寻找一个最小的数,本次循环后将最小的数与第一个数交换位置

优化:当寻找后最小下标不变,则不交换

public static void selectSort(int []arr){
    //假设数组内第一个元素为最小值,所以最小下标为0
    int minIndex = 0;
    //临时量,用于交换
    int temp = 0;
    for (int i = 0; i < arr.length; i++) {
        minIndex = i;
        for (int j = i+1; j < arr.length; j++){
            //如果后面数比最小数大
            if(arr[minIndex] > arr[j]){
                //更该最小下标
                minIndex = j;
            }
        }
        //有比第一个更小的,交换(优化)
        if (minIndex != i){
            //将最小值放在第一个位置
            temp = arr[minIndex];
            arr[minIndex] = arr[i];
            arr[i] = temp;
        }
    }

}

5.3插入排序

将数组分为两部分:有序和无序,(第一个数为有序)

将后面无序的数,向前依次对有序数比较(无序数小则有序数一直向后覆盖)

最后将最前面的数补位那个无序数。

public static void insertSort(int []arr){
        for (int i = 1; i < arr.length; i++){
            //存储当前值
            int value = arr[i];
            int j =i;
            for(j = i-1;j>=0&&arr[j]>value; j--){
                //覆盖后面较小值
                arr[j+1] =arr[j];
            }
            arr[j+1] = value;
        }
    }

5.4希尔排序

将数组分组计算,每次分为length/2组(步长为length/2)

public static void shellSort(int []arr){
    //step步长
    for (int step = arr.length/2; step>0; step/=2){
        //根据步长计算应该和谁比较
        for (int i = step; i < arr.length; i++){
            int temp =arr[i];
            int j = i;
            for (j = i-step;j >= 0&&arr[j] > temp; j-=step){
                arr[j+step] = arr[j];
            }
            arr[j+step] = temp;
        }
    }
}

5.5快速排序

传入要排序的头和尾,将第一个值作为标准。

两头向中间遍历,期间将小于标准值的和大于标准值的数互换,直到相遇,=(先右,在左),然后将标准值归正确位置

然后递归标准值左边的,递归标准值右边的。

递归出口:当传入的头小于等于尾时

public static void quickSort(int []arr,int left, int right){
    if (left >= right){
        return;
    }
    //第一个值作为标准值
    int standard = arr[left];
    //右边寻找的哨兵
    int rightFind =right;
    //左边寻找的哨兵
    int leftFind = left;
    //存储临时变量
    int temp = 0;

    while (leftFind < rightFind){
        //右边有值大于等于标准值
        while (arr[rightFind] >= standard && leftFind<rightFind){
            //哨兵左移一个
            rightFind--;
        }
        //左边有值小于等于标准值
        while (arr[leftFind] <= standard && leftFind<rightFind){
            //哨兵左移一个
            leftFind++;
        }
        //找到对应值,交换
        if (leftFind < rightFind){
            temp = arr[rightFind];
            arr[rightFind] = arr[leftFind];
            arr[leftFind] = temp;
        }
    }
    //将当前值与标准值互换位置(将标准值归位)
    arr[left] = arr[rightFind];
    arr[rightFind] =standard;
    printArr(arr);
    //递归
    quickSort(arr,left,rightFind-1);
    quickSort(arr,rightFind+1,right);
}

5.6归并排序

类似于二叉树,一直递归到最低。

当每层结束的时候名,将该层的arr排序

排序后记得将排序后的元素复制到arr中

/**
 * 归并
 * @param arr 原始数组
 * @param left  数组的最左端
 * @param right 数组的最右端
 * @param temp  临时数组,用于还原
 */
public static void mergeSort(int []arr, int left, int right,int []temp){
    if (left >= right){
        return;
    }
    //对左边进行递归
    int mid = (left+right)/2;
    //System.out.println("左");
    mergeSort(arr,left,mid,temp);

    //右边
    //System.out.println("右");
    mergeSort(arr,mid+1,right,temp);

    merge(arr,left,mid,right,temp);
    printArr(arr);

    //System.out.println("完");
}
public static void merge(int[] arr,int left,int mid,int right, int[] temp){
    //左边序列开始索引
    int i = left;
    //右边
    int j = mid+1;
    //temp的开始索引
    int k = 0;
    while (i<=mid&&j<=right){
        //将小的值放入temp中,并且下标都后移一个
        if(arr[i] < arr[j]){
            temp[k++] = arr[i++];
        }
        else {
            temp[k++] = arr[j++];
        }
    }
    //此时如果有数组有剩余
    while (i <= mid){
        temp[k++]= arr[i++];
    }
    //此时如果有数组有剩余
    while (j <= right){
        temp[k++]= arr[j++];
    }
    //此时temp已经为排序好的数组,将temp内元素转移到arr中
    for (int x = 0; x < k;x++){
        arr[left+x] = temp[x];
    }
}

5.7基数排序

比较数组内各数的个位,然后放入对应桶内,取出放回数组,再比较十位。。。

注意:取出后应将计算桶内元素数的数组清零

public static void baseSort(int []arr){
    //计算数组内最大值
    int max = arr[0];
    for (int i = 0; i < arr.length; i++) {
        if (max<arr[i]){
            max = arr[i];
        }
    }
    //计算最大值的位数(个、十、百。。)
    int exponent = (max+"").length();
    //10个临时存放数组的桶,每个桶最多存length个
    int[][] bucketArray = new int[10][arr.length];

    //存放每个桶内的元素数量
    int []bucketEleCount = new int[10];
    for (int i = 1, n = 1; i < exponent+1; i++,n*=10){
        for (int arrIndex = 0; arrIndex < arr.length; arrIndex++){
            //当前循环需要比较的尾数
            int tailNum = ((arr[arrIndex])/n) %10;
            //将元素放入临时桶内(bucketEleCount[tailNum]的初始值为0,因此需要+1来确定下次放入的位置)
            bucketArray[tailNum][bucketEleCount[tailNum]] = arr[arrIndex];
            bucketEleCount[tailNum]++;
        }
        //依次取出元素,放回到数组内
        int arrIndex = 0;
        for (int bucketIndex = 0; bucketIndex < 10; bucketIndex++){
            //如果计数的桶不为空
            if (bucketEleCount[bucketIndex] != 0){
                for (int j = 0; j < bucketEleCount[bucketIndex]; j++){
                    //取出元素
                    arr[arrIndex++] = bucketArray[bucketIndex][j];
                    //将桶内元素清空(也可以不清空,下次会覆盖,用bucketEleCount来计算得出应该取多少元素)
                    bucketArray[bucketIndex][j] = 0;
                }

            }
            bucketEleCount[bucketIndex] = 0;

        }
    }
}

5.8堆排序

/**
 * 堆排序
 */
public static void heapSort(int []arr){
    //1.将原数组转换为大顶堆
    //arr.length/2-1:最后一个叶子节点
    for(int i = arr.length/2-1; i>=0; i--){
        adjustHeap(arr,i,arr.length);
    }
    //2.已经为大顶堆
    //将最大数放在数组最后,然后继续将数组换位大顶堆
    int temp = 0;
    for (int j = arr.length-1; j>0;j--){
        //交换
        temp = arr[j];
        arr[j] = arr[0];
        arr[0] = temp;

        //继续转换为大顶堆,此时只有顶元素需要调整
        adjustHeap(arr,0,j);
    }
}
/**
 * 调整为大顶堆
 * 将数组看做为一个 顺序化树
 * @param arr
 * @param i 当前非叶子节点对应下标,要调整的数的根节点
 * @param length 数组长度
 */
public static void adjustHeap(int[] arr,int i, int length){
    //保存当前非叶子节点
    int temp = arr[i];
    //将以i下标为节点的树调整为大顶堆,循环
    for (int k = 2*i+1; k<length; k=2*k+1){
        //右节点大于左节点
        if(k+1<length && arr[k+1] > arr[k]){
            k++;
        }
        //子节点大于父节点
        if(arr[k] > temp){
            //赋值给父节点
            arr[i] = arr[k];
            //下次从i节点开始
            i = k;
        }
        //父节点大(当前子树已经为大顶堆)
        else {
            break;
        }
    }
    //将原来父节点的值,赋值给下次要开始遍历的值
    arr[i] = temp;
}

六、查找

6.1二分查找

/**
 * 二分查找
 * @param arr
 * @param left
 * @param right
 * @param searchNum 要查找的值
 * @return
 */
public static int binarySearch(int []arr, int left, int right, int searchNum){
    if(left > right){
        //没找到
        return -1;
    }
    //中间值
    int mid = (left+right)/2;
    if(searchNum == arr[mid]){
        //返回下标
        return mid;
    }
    else if (searchNum < arr[mid]){
        return binarySearch(arr,left,mid-1,searchNum);
    }
    else {
        return binarySearch(arr,mid+1,right,searchNum);
    }

}

6.2插值查找

适用于均匀的递增递减序列,并且查找数不能大于最大值,不能小于最小值

image-20210731143909467

/**
 * 插值查找
 * @param arr
 * @param left
 * @param right
 * @param searchNum
 * @return
 */
public static int interpolationSearch(int []arr, int left, int right, int searchNum){
    //防止数组越界加入额外校验
    if(left > right || searchNum < arr[left] || searchNum > arr[right]){
        //没找到
        return -1;
    }
    //中间值(自适应)
    int mid = left + (right - left) *(searchNum - arr[left])/(arr[right]-arr[left]);
    if(searchNum == arr[mid]){
        //返回下标
        return mid;
    }
    else if (searchNum < arr[mid]){
        return interpolationSearch(arr,left,mid-1,searchNum);
    }
    else {
        return interpolationSearch(arr,mid+1,right,searchNum);
    }

}

6.3斐波那契(黄金分割)

//因为后面我们mid=low+F(k-1)-1,需要使用到斐波那契数列,因此我们需要先获取到一个斐波那契数列
//非递归方法得到一个斐波那契数列
public static int[] fib() {
int[] f = new int[maxSize];
f[0] = 1;
f[1] = 1;
for (int i = 2; i < maxSize; i++) {
f[i] = f[i – 1] + f[i – 2];
}
return f;
}

//编写斐波那契查找算法
//使用非递归的方式编写算法
/**
 * 
 * @param a  数组
 * @param key 我们需要查找的关键码(值)
 * @return 返回对应的下标,如果没有-1
 */
public static int fibSearch(int[] a, int key) {
	int low = 0;
	int high = a.length - 1;
	int k = 0; //表示斐波那契分割数值的下标
	int mid = 0; //存放mid值
	int f[] = fib(); //获取到斐波那契数列
	//获取到斐波那契分割数值的下标
	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]
			//因为 前面有 f[k-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-1] = 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;
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/115184.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • QtreeWidget简介「建议收藏」

    QtreeWidget简介「建议收藏」设置右键菜单并实现添加一个子项删除一个子项的功能这样有两个缺点1.只能添加特定的子项。2.不能实现不同层级节点的不同菜单。dialog.cppwidget.cppwidget.h还可以通过TYPE属性来确定每一个节点的层级。代码实现mainwindow.cppmainwindow.hdialog.cppdialog.h参考博客…

    2022年10月30日
  • Linux系统官网下载「建议收藏」

    Linux系统官网下载「建议收藏」CentOS-6.9-x86_64-bin-DVD1.isohttp://archive.kernel.org/centos-vault/6.9/isos/x86_64/CentOS-6.9-x86_

  • IPHONE接口定义

    IPHONE接口定义苹果公司使用了一家名叫JAE公司的接插件,型号为DD1.这个接口有30针iphone接口定义英文版的:ThisconnectorisusedoniPod(startingfrom3rdgeneration)andiPhone.ItisusedtoconnecttheiPodoriPhonetovariousdevices:PC(viaUS

  • sql存储过程实例详解_sql server创建存储过程

    sql存储过程实例详解_sql server创建存储过程问题提出  我使用过几次SQLServer,但所有与数据库的交互都是通过应用程序的编码来实现的。我不知到在哪里使用存储过程,也不了解实现存储过程需要做哪些工作。希望能详细说明。  存储过程是存储于数据库中的一组T-SQL语句。有了存储过程之后,与数据库的交互就没有必要在程序中写一堆的SQL语句,而只需用一条语句调用适当的存储过程来完成就可以了。另外,由于代码是存储在数据库

  • Java中怎样由枚举常量的ordinal值获得枚举常量对象

    Java中怎样由枚举常量的ordinal值获得枚举常量对象

    2021年12月15日
  • 亿能bms上位机_上位机软件 上位机PC软件 bms电池管理系统测试系统软件「建议收藏」

    亿能bms上位机_上位机软件 上位机PC软件 bms电池管理系统测试系统软件「建议收藏」上位机软件上位机PC软件bms电池管理系统测试系统软件上海鸣野软件开发公司从创立伊始就立足于差异化竞争,只做自己有竞争优势的事,13391389262,,在上位机软件,上位机开发,bms电池管理系统测试系统软件,上位机软件外包积累了丰富的开发经验,通过学习和创新,不断地扩大和加强公司在行业中的优势,是企业发展之道。关注别人所不关注的,看到别人所看不到的,做到别人所做不到的,是鸣野软件开发公司*…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号