基础算法面试题

一、排序

小到大排序

1、冒泡排序

冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

时间复杂度

  1. 最坏情况下,需要进行n-1轮比较,每轮比较需要比较的次数为 n-i-1,因此总的比较次数为 (n-1)+(n-2)+...+1 = n(n-1)/2,即O(n^2)。
  2. 最好情况下,待排序的序列已经有序,只需要进行一轮比较,不需要进行任何交换操作,时间复杂度为 O(n)。
  3. 平均情况下,冒泡排序的时间复杂度也为 O(n^2)。

基础版本 

    // 外循环每次循环结束确定一个最大值,然后放到末尾
    @Test
    public void aaa() {
        int[] ints = new int[]{10, 832, 82, 101, 0, 90};
        for(int i = 0; i < ints.length-1; i++) 
        {
            for(int j = 0; j < ints.length - 1 - i; j++) 
            {
                if(ints[j] > ints[j+1]){
                    int temp = ints[j];
                    ints[j] = ints[j+1];
                    ints[j+1] = temp;
                }
            }
        }
        for (int anInt : ints) {
            System.out.println(anInt);
        }
    }
}

优化版本

    @Test
    public void maopao() {
        int[] ints = new int[]{10, 832, 82, 101, 0, 90};
        bubbleSort(ints);
        for (int anInt : ints) {
            System.out.print(anInt + "  ");
        }
    }

    public static void bubbleSort(int[] arr) {
        int n = arr.length;
        boolean swapped;

        for (int i = 0; i < n - 1; i++) {
            swapped = false;

            for (int j = 0; j < n - i - 1; j++) {
                if (arr[j] > arr[j + 1]) {
                    // 交换arr[j]和arr[j + 1]
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                    swapped = true;
                }
            }

            // 如果没有发生元素交换,说明数组已经有序,提前结束排序
            if (!swapped) {
                break;
            }
        }
    }

2、选择排序

表现最稳定的排序算法之一,因为无论什么数据进去都是O(n2)的时间复杂度

它的工作原理:首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

基础版本 

    @Test
    public void xuanze() {
        int[] ints = new int[]{10, 832, 82, 101, 0, 90};

        for (int i = 0; i < ints.length - 1; i++) {
            // 假设当前位置的元素为最小值
            int minNum = i;
            for (int j = i + 1; j < ints.length; j++) {
                if (ints[minNum] > ints[j]) {
                    minNum = j;
                }
            }

            int temp = ints[i];
            ints[i] = ints[minNum];
            ints[minNum] = temp;
        }

        for (int anInt : ints) {
            System.out.print(anInt + " ");
        }
    }

优化版本

    @Test
    public void xuanze() {
        int[] ints = new int[]{10, 832, 82, 101, 0, 90};

        for (int i = 0; i < ints.length - 1; i++) {
            // 假设当前位置的元素为最小值
            int minNum = i;
            for (int j = i + 1; j < ints.length; j++) {
                if (ints[minNum] > ints[j]) {
                    minNum = j;
                }
            }

            // 如果最小值索引发生变化,则进行交换操作
            if (minNum != i) {
                int temp = ints[i];
                ints[i] = ints[minNum];
                ints[minNum] = temp;
            }
        }

        for (int anInt : ints) {
            System.out.print(anInt + " ");
        }
    }

3、快速排序

快速排序从小到大排序:在数组中随机选一个数(默认数组首个元素),数组中小于等于此数的放在左边,大于此数的放在右边,再对数组两边递归调用快速排序,重复这个过程。

    @Test
    public void kspx() {
        int[] ints = new int[]{0, 10, 10, 10, 832, 82, 101, 1, 90, 10000};
        quickSort(ints, 0, ints.length - 1);
        for (int anInt : ints) {
            System.out.println(anInt + "  ");
        }
    }

    public void quickSort(int[] arr, int minNum, int maxNum) {
        if (minNum > maxNum) {
            return;
        }
        int i = minNum;
        int j = maxNum;
        int base = arr[i];
        while (i != j) {
            // 注意:要先从右边开始
            // 原因:
            // 1、当首先从右边开始先执行时,循环的条件是:最后i、j 停留的位置的值肯定是要 小于 base 的 此时交换索引 j 和最左边元素base 符合将小于base的值放到base左边这一条件。
            // 2、当首先从左边开始执行时,情况就是相反的
            while (arr[j] >= base && i < j) {
                j--;
            }
            while (arr[i] <= base && i < j) {
                i++;
            }
            if (i < j) {
                //  数值交换
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
        arr[minNum] = arr[i];
        arr[i] = base;
        quickSort(arr, minNum, i - 1);
        quickSort(arr, i + 1, maxNum);
    }

二、二分法查找

二分法查找是在一组有序数据中寻找目标值,并获得目标值所在的下标位置

二分法查找也叫折半查找,基本逻辑是每次都向中间位置找,然后缩小一半范围再向中间位置找,请看下面的示例:

a

    @Test
    public void aaa() {
        // 二分法是对于一个排好序的数组进行查找
        int[] ints = new int[]{10, 32, 82, 101, 102, 190};
        int min = 0;
        int max = ints.length - 1;
        int centre;
        // 查找的元素
        int number = 102;
        while (min <= max) {
            centre = (min + max) / 2;
            if (number < ints[centre]) {
                max = centre - 1;
            } else if (number > ints[centre]) {
                min = centre + 1;
            } else {
                System.out.println(centre);
                return;
            }
        }
        System.out.println("不存在");
    }

三、扁平数据转Tree

1、递归实现

/**
 * @Description 菜单实体类
 * @Author syh
 * @Date 2023/12/17 14:53
 */
@Data
@NoArgsConstructor
public class MenuInfos {

    public MenuInfos(String id, String menuName, String parentMenuId) {
        this.id = id;
        this.menuName = menuName;
        this.parentMenuId = parentMenuId;
    }

    // 菜单id
    private String id;

    // 菜单名称
    private String menuName;

    // 父id
    private String parentMenuId;

    private List<MenuInfos> childrenList = new ArrayList<>();  // 一定要new出来,否者使用时空指针
}
    public static void main(String[] args) {
        List<MenuInfos> allMenuInfos = new ArrayList<>();
        allMenuInfos.add(new MenuInfos("1", "系统设置", "-1"));
        allMenuInfos.add(new MenuInfos("2", "数据统计", "-1"));
        allMenuInfos.add(new MenuInfos("3", "用户管理", "1"));
        allMenuInfos.add(new MenuInfos("4", "部门管理", "1"));
        allMenuInfos.add(new MenuInfos("5", "菜单管理", "1"));
        allMenuInfos.add(new MenuInfos("6", "用户登录统计", "2"));
        allMenuInfos.add(new MenuInfos("7", "打卡记录统计", "2"));
        allMenuInfos.add(new MenuInfos("8", "用户密码重置", "3"));
        //                                    传入树形集合,  最高父id
        List<MenuInfos> menuInfos = makeTree(allMenuInfos, "-1");
        System.out.println(menuInfos);
    }

    /**
     * 递归菜单
     */
    public static List<MenuInfos> makeTree(List<MenuInfos> menusList, String parentId) {
        // 后辈子类
        List<MenuInfos> children = menusList.stream().filter(
                x -> x.getParentMenuId().equals(parentId)).collect(Collectors.toList()
        );

        // 后辈非子类
        List<MenuInfos> youngerGeneration = menusList.stream().filter(
                x -> !x.getParentMenuId().equals(parentId)).collect(Collectors.toList()
        );
        
        children.forEach(x -> {
                    List<MenuInfos> menuInfos = makeTree(youngerGeneration, x.getId());
                    x.setChildrenList(menuInfos);
                }
        );

        return children;
    }

2、非递归实现 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

S Y H

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

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

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

打赏作者

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

抵扣说明:

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

余额充值