2021-11-11剑指offer--排序

JZ3 数组中重复的数字

题目地址
描述
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任一一个重复的数字。 例如,如果输入长度为7的数组[2,3,1,0,2,5,3],那么对应的输出是2或者3。存在不合法的输入的话输出-1

数据范围:0 ≤ n ≤10000
进阶:时间复杂度0(n),空间复杂度0(n)
示例1

输入:
[2,3,1,0,2,5,3]
返回值:
2
说明:
2或3都是对的 

代码
采用哈希数组arr,arr[element]=count表示元素element出现次数为count

import java.util.*;
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param numbers int整型一维数组 
     * @return int整型
     */
    public int duplicate (int[] numbers) {
        // write code here
        int[] arr=new int[numbers.length];
        for(int i=0;i<numbers.length;i++){
            arr[numbers[i]]++;
            if(arr[numbers[i]]>=2){
                return numbers[i];
            }
        }
        return -1;
    }
}

JZ51 数组中的逆序对

题目地址
描述
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P mod 1000000007

数据范围
对于 50% 的数据, size ≤ 10^4
对于100的数据, size ≤ 10^5

数组中所有数字的值满足0 ≤ val ≤ 1000000

要求:空间复杂度 O(n),时间复杂度 O(nlogn)

输入描述
题目保证输入的数组中没有的相同的数字
示例1

输入:
[1,2,3,4,5,6,7,0]
返回值:
7

示例2

输入:
[1,2,3]
返回值:
0

算法思路
此处采用归并排序的方法,不知道的小伙伴可以先了解下~
首先简单介绍下归并排序:
归并排序讲究先分后并

先分:将数组分为两个子数组,子数组继续往下分,直到不能再分。
后并:从最小的数组按照顺序合并,从小到大或从大到小依次向上合并,直到获得最后的数组。

利用归并的方法进行统计分析,主要是在合并的时候统计。
例如
在合并 {4 ,5} {1 , 2} 的时候,首先我们判断 1 < 4,此时统计出逆序对为2。这利用了数组的部分有序性。因为我们知道 {4 ,5} 这个数组必然是有序的,因为是合并上来的。此时当 1比4小的时候,证明4以后的数也都比1大,此时就构成了从4开始到 {4,5}这个数组结束,这么多个逆序对(2个),此时利用一个临时数组,将1存放起来,接着比较2和4的大小,同样可以得到有2个逆序对,于是将2也放进临时数组中,此时右边数组已经完全没有元素了,则将左边剩余的元素全部放进临时元素中,最后将临时数组中的元素放进原数组对应的位置。
(以上借鉴牛客Dylan博主的思路分享)
代码

public class Solution {
    public int InversePairs(int [] array) {
        int mod=1000000007;
        return InversePairs(array,0,array.length-1,mod);
    }
    public int InversePairs(int[] array,int l,int r,int mod){
        while(l<r){
            int mid=(l+r)/2;
            return (InversePairs(array,l,mid,mod)%mod
                +InversePairs(array,mid+1,r,mod)%mod
                +merge(array,l,mid,r,mod)%mod)%mod;
        }
        return 0;
    }
    public int merge(int[] array,int l,int mid,int r,int mod){
        int i=l,j=mid+1;
        int index=0;
        int[] kept=new int[r-l+1];
        int res=0;
        while(i<=mid&&j<=r){
            if(array[i]<=array[j]){
                kept[index++]=array[i];
                i++;
            }else{
                res=(res+(mid-i+1)%mod)%mod;
                kept[index++]=array[j];
                j++;
            }
        }
        while(i<=mid){
            kept[index++]=array[i++];
        }
        while(j<=r){
            kept[index++]=array[j++];
        }
        for(int k=0;k<kept.length;k++){
            array[k+l]=kept[k];
        }
        return res;
    }
}

JZ40 最小的K个数

题目地址
描述
给定一个长度为 n 的可能有重复值的数组,找出其中不去重的最小的 k 个数。例如数组元素是4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4(任意顺序皆可)。
数据范围:0≤k,n≤10000,数组中每个数的大小0≤val≤1000
要求:空间复杂度 O(n) ,时间复杂度 O(nlogn)

示例1

输入:
[4,5,1,6,2,7,3,8],4 
返回值:
[1,2,3,4]
说明:
返回最小的4个数即可,返回[1,3,2,4]也可以 

示例2

输入:
[1],0
返回值:
[]

示例3

输入:
[0,1,2,1,2],3
返回值:
[0,1,1]

代码
方法一:升序排序,然后找出最小的k个。
方法二:利用堆,设置一个大顶堆maxHeap,遍历数组中的元素,如果遍历到元素elem:

  • 若maxHeap.size()<k:maxHeap.add(elem)
  • 若maxHeap.size()==k:
    • 若maxHeap.peek()>elem:maxHeap弹出栈顶元素 并 加入新元素elem

遍历结束后,栈中存放的就是前k小的元素。

import java.util.*;


public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        return GetLeastNumbers2(input,k);
    }
    //方法一:排序
    public ArrayList<Integer> GetLeastNumbers1(int[] input,int k){
        Integer[] a=new Integer[input.length];
        for(int i=0;i<input.length;i++){
            a[i]=input[i];
        }
        Arrays.sort(a,new Comparator<Integer>(){
            public int compare(Integer o1,Integer o2){
                return o1-o2;
            }
        });
        ArrayList<Integer> res=new ArrayList();
        for(int i=0;i<k;i++){
            res.add(a[i]);
        }
        return res;
    }
    //方法二:大顶堆  维持大小最大为k
    public ArrayList<Integer> GetLeastNumbers2(int[] input,int k){
        ArrayList<Integer> res=new ArrayList<>();
        if(input==null || k<=0){
            return res;
        }
        PriorityQueue<Integer> maxheap=new PriorityQueue<>(new Comparator<Integer>(){
            public int compare(Integer o1,Integer o2){
                return o2-o1;
            }
        });
        for(int i=0;i<input.length;i++){
            if(maxheap.size()<k){
                maxheap.add(input[i]);
            }else if(maxheap.peek()>input[i]){
                    maxheap.poll();
                    maxheap.add(input[i]);
             }
        }
        while(!maxheap.isEmpty()){
            res.add(maxheap.poll());
        }
        return res;
    }
}

JZ41 数据流中的中位数

题目地址
描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

数据范围:数据流中数个数满足1 ≤ n ≤ 1000 ,大小满足 1 ≤ val ≤ 1000

进阶: 空间复杂度0(n) , 时间复杂度 0(nlogn)

示例1

输入:
[5,2,3,4,1,6,7,0,8]
返回值:
"5.00 3.50 3.00 3.50 3.00 3.50 4.00 3.50 4.00 "
说明:
数据流里面不断吐出的是5,2,3...,则得到的平均数分别为5,(5+2)/2,3...   

示例2

输入:
[1,1,1]
返回值:
"1.00 1.00 1.00 "

代码
方法一:先排序然后找出中位数,代码略
方法二:采用大顶堆maxHeap 和小顶堆minHeap的方法,小顶堆存放一半的元素,大顶堆存放另一半,若无法平分,大顶堆多存一个。

  • 若大小堆元素个数相同,中位数=(maxHeap.peek()+minHeap.peek())/2.0;
  • 若大小堆元素个数不同,中位数=maxHeap.peek();
import java.util.*;
public class Solution {
    private PriorityQueue<Integer> minHeap=new PriorityQueue<>((o1,o2)->o1-o2);//right(1/2)
    private PriorityQueue<Integer> maxHeap=new PriorityQueue<>((o1,o2)->o2-o1);//left(1/2 + 1)
    public void Insert(Integer num) {
        if(maxHeap.isEmpty() || num<maxHeap.peek()){
            maxHeap.add(num);
        }else{
            minHeap.add(num);
        }
        if(maxHeap.size() == minHeap.size() + 2)
            minHeap.add(maxHeap.poll());
        if(minHeap.size() == maxHeap.size() + 2)
            maxHeap.add(minHeap.poll());
    }
    public Double GetMedian() {
        if(maxHeap.size() != minHeap.size()){
            return maxHeap.size()>minHeap.size()?((double)maxHeap.peek()):((double)minHeap.peek());
        }else{
            return ((double)maxHeap.peek()+(double)minHeap.peek())/2.0;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值