面统 -- 21年面试中的数据结构与算法题(现场写)

请描述快速排序和归并排序的思路 (京东)

快速排序实现可分为以下几步:

  1. 在数组中选一个基准数(通常为数组第一个)
  2. 将数组中小于基准数的数据移到基准数左边,大于基准数的移到右边
  3. 对于基准数左、右两边的数组,不断重复以上两个过程,直到每个子集只有一个元素,即为全部有序

时间复杂度为:O(nlogn)

    public void quickSort(int[] nums,int begin,int end){
        if (begin >= end) return;
        int temp = nums[begin];
        int i = begin,j = end;
        while (i != j){
            while (nums[j] >= temp && i != j) j--;
            if (i != j) {
                nums[i] = nums[j];
                i++;
            }
            while (nums[i] <= temp && i != j) i++;
            if (i != j){
                nums[j] = nums[i];
                j--;
            }
        }
        nums[i] = temp;
        quickSort(nums,begin,i-1);
        quickSort(nums,i+1,end);
    }

归并排序
    是一类与插入排序、交换排序、选择排序不同的另一种排序方法。归并的含义是将两个或两个以上的有序表合并成一个新的有序表。归并排序有多路归并排序、两路归并排序 , 可用于内排序,也可以用于外排序。这里仅对内排序的两路归并方法进行讨论。

一、两路归并排序算法思路
分而治之(divide - conquer);每个递归过程涉及三个步骤
第一, 分解: 把待排序的 n 个元素的序列分解成两个子序列, 每个子序列包括 n/2 个元素.
第二, 治理: 对每个子序列分别调用归并排序MergeSort, 进行递归操作
第三, 合并: 合并两个排好序的子序列,生成排序结果.
时间复杂度O(nlogn)

public static void mergeSort(int[] data) {
        sort(data, 0, data.length - 1);
    }

    public static void sort(int[] data, int left, int right) {
        if (left >= right)
            return;
        // 找出中间索引
        int center = (left + right) / 2;
        // 对左边数组进行递归
        sort(data, left, center);
        // 对右边数组进行递归
        sort(data, center + 1, right);
        // 合并
        merge(data, left, center, right);
        print(data);
    }

    /**
     * 将两个数组进行归并,归并前面2个数组已有序,归并后依然有序
     * @param data 数组对象
     * @param left 左数组的第一个元素的索引
     * @param center 左数组的最后一个元素的索引,center+1是右数组第一个元素的索引
     * @param right 右数组最后一个元素的索引
     */
    public static void merge(int[] data, int left, int center, int right) {
        // 临时数组
        int[] tmpArr = new int[data.length];
        // 右数组第一个元素索引
        int mid = center + 1;
        // third 记录临时数组的索引
        int third = left;
        // 缓存左数组第一个元素的索引
        int tmp = left;
        while (left <= center && mid <= right) {
            // 从两个数组中取出最小的放入临时数组
            if (data[left] <= data[mid]) {
                tmpArr[third++] = data[left++];
            } else {
                tmpArr[third++] = data[mid++];
            }
        }
        // 剩余部分依次放入临时数组(实际上两个while只会执行其中一个)
        while (mid <= right) {
            tmpArr[third++] = data[mid++];
        }
        while (left <= center) {
            tmpArr[third++] = data[left++];
        }
        // 将临时数组中的内容拷贝回原数组中
        // (原left-right范围的内容被复制回原数组)
        while (tmp <= right) {
            data[tmp] = tmpArr[tmp++];
        }
    }

    public static void print(int[] data) {
        for (int i = 0; i < data.length; i++) {
            System.out.print(data[i] + "\t");
        }
        System.out.println();
    }

给定一个数组,返回第二大的那个数。(滴滴)

不要采用先排序再取出第二大元素,因为最快的排序算法也要O(nlogn),本解法时间复杂度为O(n)

思路:

1)假设数组中的前两个元素就是最大和第二大,即max和smax;

2)从数组的第二个元素开始遍历数组,当有元素大于max的时候,将max赋值给smax,再将最大的那个元素赋值给max;

3)如果当前元素小于max,并且大于smax,那么就让smax当前元素。

这样max和smax中存储的一直都是当前已经扫描过的所有元素的第一大和第二大了。

    public int findTheSecond(int[] nums){
        int max,smax;
        if (nums[0]>nums[1]) {
            max=nums[0];
            smax=nums[1];
        } else {
            max=nums[1];
            smax=nums[0];
        };
        for (int i=2;i<nums.length;i++){
            if (nums[i] > max) {
                smax = max;
                max = nums[i];
            }
            if (nums[i] < max && nums[i] > smax) smax = nums[i];
        }
        return smax;
    }

给定一个链表,判断链表中是否有环。 (滴滴)

    boolean isCircle(ListNode head){
        if (head == null) return false;
        ListNode slow = head, quick = head;
        while (quick.next != null){
            quick = quick.next;
            if (quick.next != null) {
                quick = quick.next;
            }else {
                return false;
            }
            slow = slow.next;
            if (quick == slow) return true;
        }
        return false;
    }

接雨水 (字节 小红书)

    int trap(int[] height){
        int sum = 0;
        int[] max_left = new int[height.length];
        int[] max_right = new int[height.length];
        
        for (int i = 1; i < height.length - 1; i++) {
            max_left[i] = Math.max(max_left[i - 1], height[i - 1]);
        }
        for (int i = height.length - 2; i >= 0; i--) {
            max_right[i] = Math.max(max_right[i + 1], height[i + 1]);
        }
        for (int i = 1; i < height.length - 1; i++) {
            int min = Math.min(max_left[i], max_right[i]);
            if (min > height[i]) {
                sum = sum + (min - height[i]);
            }
        }
        return sum;
    }

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。(字节)

要求

  • 你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值