【面试排序练习】——随笔记录(二)

一、小范围排序题

题意:

所谓的小范围排序题指的是,已知一个几乎有序的数组,几乎有序的意思是,如果把数组排好顺序,每个元素移动的距离不超过k,并且k的大小远远小于数组的长度。问选择什么样的方法好。

分析:

1.时间复杂度为O(n)

计数排序基数排序,由于桶排序类排序算法的特点,当我们不知道数据的具体范围时,无法使用。所以不考虑。

2.时间复杂度为O(n²)

其中冒泡排序选择排序的时间复杂度不会根据数组的排列改变,始终都是O(n²)

插入排序的时间复杂度在小范围排序的情况下可以优化到O(n*k)

3.时间复杂度为O(n*㏒n)

其中快速排序归并排序的时间复杂度不会受到影响

其实这一题主要使用的是一种改进后的堆排序算法。时间复杂度能过降到O(n*㏒k)。

思路:

因为排序过后每个元素移动的距离不会超过k,那么意味着最小值一定在0位置到k-1位置中,所以我们可以首先将第0个位置到底k-1个位置的数建一个小根堆,然后将堆顶弹出,再将第k个位置的数放入堆中,调整堆,以此类推。

代码举例:

import java.util.*;

public class ScaleSort {
    public int[] sortElement(int[] A, int n, int k) {
        // write code here
        if (A == null || A.length == 0 || n < k){
            return A;
        }
        //创建小根堆
        int[] heap = getKHeap(A, k);
        //遍历数组,将k-1位置以后的数一次放入根堆并进行调整
        for (int i = k; i < n; i++) {
            //将堆顶元素弹出,放到数组中,从0号一直到n号
            A[i - k] = heap[0];
            //讲下一个元素放入小根堆
            heap[0] = A[i];
            //调整根堆
            heapify(heap, 0, k);
        }
        //下面的操作,是当循环进行到最后一个元素时,还剩最后一个n-k到n位置的小根堆,单独对这个小根堆进行操作
        for (int i = n - k; i < n; i++) {
            A[i] = heap[0];
            swap(heap, 0, k - 1);
            heapify(heap, 0, --k);
        }
        return A;
        }
        
        //创建堆结构,小根堆调整
        public void heapify(int[] arr,int index,int heapSize){
            //创建堆结构
            int left = index*2+1;
            int right = index*2+2;
            int smallest = index;
            //对堆结构进行调整
            while(left<heapSize){
                if(arr[left]<arr[index]){
                    smallest = left;
                }
                if(right<heapSize && arr[right]<arr[index]){
                    smallest = right;
                }
                //当最小元素的值位置发生改变时,调整堆结构
                if(smallest != index){
                    swap(arr,smallest,index);
                }else{
                    break;
                }
                //重新调整标签指向
                index = smallest;
                left = index*2+1;
                right = index*2+2;
            }
        }
        
        //插入新元素到已有的堆结构中
        public void heapInsert(int[] heap,int value,int index){
            //获取值
            heap[index] = value;
            //调整堆结构
            while(index != 0){
                //定义它的父亲节点
                int parant = (index-1)/2;
                if(heap[index]<heap[parant]){
                    swap(heap,parant,index);
                    index = parant;
                }else{
                    break;
                }
            }
        }
        //创建小根堆
        public int[] getKHeap(int[] A,int k){
        int[] heap = new int[k];
        //一次遍历数组,将元素一个个放入根堆中
        for (int i = 0; i < k; i++) {
            heapInsert(heap, A[i], i);
        }
        return heap;
        }
        
        //创建比较函数
        public void swap(int[] arr,int index1,int index2){
            int tmp = arr[index1];
            arr[index1] = arr[index2];
            arr[index2] = tmp;
        }
}

      

二、重复值判断

题意:

判断数组中重复的数的格式,并且要求空间复杂度为O(1)

分析:

如果这个题没有要求空间复杂度,那么我们使用哈希表来实现的,但是规定了空间复杂度,所以我们需要将数组排序,然后进行遍历,从而获得结果。

思路:

我们可以使用空间复杂度为O(1)的算法入冒泡排序,选择排序,插入排序,希尔排序,堆排序;这个地方我们如果使用到的是堆排序,但是如果堆排序使用递归实现的话,空间复杂度为O(logn)。

代码举例:

import java.util.*;

public class Checker {
    public boolean checkDuplicate(int[] a, int n) {
        // write code here
        if (a == null || n == 0) {
            return false;
        }
        heapSort(a);
        for (int i = 1; i < n; i++) {
            if (a[i] == a[i - 1]) {
                return true;
            }
        }
        return false;
    }
    
    //创建堆结构
    public  void heapSort(int[] arr) {
        //将元素插入堆中
        for (int i = 0; i != arr.length; i++) {
            heapInsert(arr, i);
        }
        //遍历堆,调整
        for (int i = arr.length - 1; i != 0; i--) {
            swap(arr, 0, i);
            heapify(arr, 0, i);
        }
    }
    
    
    //创建堆结构,小根堆调整
        public void heapify(int[] arr,int index,int heapSize){
            //创建堆结构
            int left = index*2+1;
            int right = index*2+2;
            int smallest = index;
            //对堆结构进行调整
            while(left<heapSize){
                if(arr[left]<arr[index]){
                    smallest = left;
                }
                if(right<heapSize && arr[right]<arr[index]){
                    smallest = right;
                }
                //当最小元素的值位置发生改变时,调整堆结构
                if(smallest != index){
                    swap(arr,smallest,index);
                }else{
                    break;
                }
                //重新调整标签指向
                index = smallest;
                left = index*2+1;
                right = index*2+2;
            }
        }
        
        //插入新元素到已有的堆结构中
        public void heapInsert(int[] heap,int index){
            //调整堆结构
            while(index != 0){
                //定义它的父亲节点
                int parant = (index-1)/2;
                if(heap[index]<heap[parant]){
                    swap(heap,parant,index);
                    index = parant;
                }else{
                    break;
                }
            }
        }
        
        //创建比较函数
        public void swap(int[] arr,int index1,int index2){
            int tmp = arr[index1];
            arr[index1] = arr[index2];
            arr[index2] = tmp;
        }
}

 

三、有序数组合并

题意:

现在有两个有序数组,分别为a,和b,此时要将这两个数组合并为一个有序数组。合并后的数组正好可以容纳这两个数组的全部元素。

分析及思路:

这样要求我们首先应该从两个数组的后面向前面比较,因为这样就不会因为插入的顺序影响了比较。

代码举例:

import java.util.*;

public class Merge {
    public int[] mergeAB(int[] A, int[] B, int n, int m) {
        // write code here
        int i = n-1;
        int j = m-1;
        int k = m+n-1;
        //循环遍历比较大小,插入数组
        while(i>=0 && j>=0){
            if(A[i]>B[j]){
                A[k]=A[i];
                i--;
            }else{
                A[k]=B[j];
                j--;
            }
            k--;
        }
        //当A中的元素全部比较完,但是B中还有元素时,将B中的元素直接转移到数组中
        while(j>=0){
            A[k--]=B[j--];
        }
        return A;
    }
}

 

 

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值