高级算法第一次课堂练习A班

1.按数值个数排序

描述

给定整数数组,请根据元素的频率对数组进行排序。 例如,如果输入数组是{2,3,2,4,5,12,12,2,3,3,3,12},则将该数组修改为{3,3,3,3,2,2, 2,12,12,4,5}。 如果两个元素的频率相同,则以升序打印。

Solution

按出现频次排序,那么我们只需要统计每个数字出现的次数,以数字出现的次数为key进行排序,最后从头到尾输出排序后的数字个数。

时间复杂度 O(nlogn)

空间复杂度O(n)

Python

import collections
# 对数列按照出现频次排序
def frecuncySort(nums):
    return [num * times for num, times in collections.Counter(nums).most_common()]

# IO输入输出
N = int(input())
for i in range(N):
    l = input()
    nums = list(map(int, input().split()))
    nums = map(str, frecuncySort(nums))
    out = ' '.join(nums)
    print(out)

Java

    public static List<Long> frecuncySort(List<Long> nums){
        // 计算频次,获取频次与数字对应的pairs
        Map<Long, Long> count = nums.stream().collect(Collectors.groupingBy(i -> i, Collectors.counting()));
        List<Pair> pairs = count.keySet().stream().map(key -> new Pair(key, count.get(key))).collect(Collectors.toList());

        // 按照频次对数字排序
        Collections.sort(pairs, (p1, p2) -> {
            // 频次相同按照数字大小排序
            if (p1.cnt == p2.cnt)
                return p1.value > p2.value ? 1:-1;
            return p1.cnt < p2.cnt ? 1:-1;
        });

        // 输出
        for(int i = 0; i < nums.size(); i++){
            nums.set(i, pairs.get(0).value);
            pairs.get(0).cnt--;
            if (pairs.get(0).cnt == 0)
                pairs.remove(0);
        }
        return nums;
    }

    public static class Pair{
        long value, cnt;

        Pair(long value, long cnt) {
            this.value = value;
            this.cnt = cnt;
        }
    }

2. 最小交换次数

描述

给定一个由N个不同的elements组成的数组,找到对数组进行排序所需的最小交换次数。您需要完成该函数,该函数返回一个表示对数组进行排序所需的最小交换次数的整数。

Solution

方法一:

遍历数组,判断当前元素是否在最终位置,否则把它交换到它的最终位置,(即每次交换至少让其中一个元素被放到其最终位置),统计总交换次数。

怎么判断当前元素是否在最终位置呢?

可以先对数组排序,得到每个数字对应最终位置的map。

python

def minswap(nums):
    # 排序
    snums = sorted(nums)
    # 位置数组
    inums = {snums[i]: i for i in range(len(snums))}
    count = 0
    for i in range(len(nums)):
        target_i = inums[nums[i]]
        while target_i != i:
            nums[i], nums[target_i] = nums[target_i], nums[i]
            count += 1
            target_i = inums[nums[i]]
    return count

java

public int minswap(List<Integer> nums){
    List<Integer> snums = new ArrayList<>();
    snums.addAll(nums);
    Collections.sort(snums);

    // 位置数组
    Map<Integer, Integer> inums = new HashMap<>();
    for (int i = 0; i<snums.size(); i++)
        inums.put(snums.get(i), i);

    int count = 0;
    for (int i = 0; i < nums.size(); i++){
        // 最终位置
        int target_i = inums.get(nums.get(i));
        while (target_i != i){
            int temp = nums.get(i);
            nums.set(i, nums.get(target_i));
            nums.set(target_i, temp);
            count++;
            target_i = inums.get(nums.get(i));
        }
        
    }
    return count;
}

方法二:

在原数组中,每个元素添加一个出边指向它最终的位置,这样画完出边后,最少会成一个环,最多n个环。 每一个环对应的交换次数等于元素数量-1,因此不需要交换的次数等于环数。交换次数 = n - 环数

python

def minswap(nums):
    # 排序
    snums = sorted(nums)
    # 位置数组
    inums = {snums[i]: i for i in range(len(snums))}
    count = 0
    visited = [False] * len(nums)
    # 计算环数
    for i in range(len(nums)):
        if visited[i]: continue
        j = i
        while not visited[j]:
            visited[j] = True
            j = inums[nums[j]]
        count += 1
    return len(nums) - count

3. 倒置个数

描述

有一个由N个实数构成的数组,如果一对元素A[i]和A[j]是倒序的,即i<j但是A[i]>A[j]则称它们是一个倒置,设计一个计算该数组中所有倒置数量的算法。要求算法复杂度为O(nlogn)

Solution

我们来回忆一下合并排序,合并排序的思路是首先对左半部分递归排序,再对右半部分递归排序,左右两部分有序后,合并这两部分,递归结束的条件是数组长度小于2,这时候不需要排序了。

合并的过程如下:

左半部分:L1 > L2 > L3 > L4

右半部分:R1 > R2 > R3 > R4 > R5

比较L1和R1的大小,如果L1 < R1, L1排为第一个,再比较R1和L2的大小....

在这个过程中,如果R1 < L1,那么R1与 L1...L4就组成了倒置对,因此我们可以在合并排序中统计这些倒置对。

python

def mergeSort(nums):
    if len(nums) < 2:
        return 0, nums
    count = 0
    mid = len(nums) // 2
    cl, l = mergeSort(nums[:mid])
    cr, r = mergeSort(nums[mid:])
    for i in range(len(nums)):
        if not r or l and l[0] < r[0]:
            nums[i] = l.pop(0)
        else:
            nums[i] = r.pop(0)
            count += len(l)
    return count + cl + cr, nums

def count(nums):
    return mergeSort(nums)[0]

4. 按照给定数组排序

描述

给定两个数组A1和A2,对A1进行排序,以使元素之间的相对顺序与A2中的相对顺序相同。 对于A2中不存在的元素。 最后按排序顺序附加它们。 还假定A2 中的元素数小于或等于A1 中的元素数,并且A2具有所有不同的元素。

Solution

统计A1数字出现的频次,遍历A2,按照统计频次输出对应数量的数,对于余下的数再排序后输出。

时间复杂度O(nlogn)

python

import collections
def mysort(A1, A2):
    count = collections.Counter(A1)
    res = []
    for num in A2:
        res += [num]*count[num]
        del count[num]
    for num in sorted(count):
        res += [num]*count[num]
    return res

java

public List<Long> mysort(List<Long> A1, List<Long> A2){
    Map<Long, Long> count = A1.stream().collect(Collectors.groupingBy(i -> i, Collectors.counting()));
    List<Long> res = A1.stream().filter(num -> !A2.contains(num)).collect(Collectors.toList());
    Collections.sort(res);
    int j = 0;
    for (Long num: A2){
        for (int i = 0; i<count.get(num); i++) {
            res.add(j, num);
            j += 1;
        }
    }
    return res;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 排序算法说明 (1)排序的定义:对一序列对象根据某个关键字进行排序; 输入:n个数:a1,a2,a3,…,an 输...
    code武阅读 694评论 0 0
  • 马云说:“梦想一定要有的,万一实现了呢” 最近心情比较复杂,我找了两个词来表达“彷徨”和“迷茫”。 我仿佛有了一种...
    李水心阅读 533评论 1 4
  • 自我上小学之后,电话号码成为了基本联络介质之一。但这时候我们还没有手机,电话通常以家庭或者办公室作为单位。交换电话...
    李子李子短信阅读 821评论 1 7
  • 最近股票跌跌不休,周围好多人都说要退出这个市场,再也不碰了,交流股票的人也越来越少。正好与这炎热的天气相反啊...
    金融大宝阅读 195评论 0 0