大数跨境
0
0

网友爆料:隔壁组来了个清华姚班的od,2018辽宁理科状元,太狠了~

网友爆料:隔壁组来了个清华姚班的od,2018辽宁理科状元,太狠了~ Julia出海营销
2024-12-13
0

最近在程序员圈子里看到一个爆料,隔壁组来了个超级牛的人物——清华姚班的OD,2018年辽宁理科状元,简直太狠了!😱

你要说这人有没有背景,那绝对是有的,不是普通的背景,是那种能在大厂HR面前“镶金边”的背景。

清华姚班、理科状元,这几个字放在简历上,HR能直接给你开门,根本不需要多看一眼别的。大家都知道,大厂HR眼睛一亮,学历硬件就是他们最想要的“金字招牌”。

但是,很多人就疑惑了,像他这样的牛人,为什么还要进OD?你想啊,清华姚班、状元这样的履历,出去随便跳槽,大厂也都是抢着要。按理说,应该高高在上,直接进入某个技术岗,结果却去了OD,这不太符合常规吧?

但反过来想,只要他想,啥大厂进不去呀,可能就是单纯的目前不想进大厂吧~

算法题:翻转对

今天我们来聊聊一个非常有意思的算法题:翻转对。如果你在面试中遇到过“翻转对”这种题目,可能会稍微有点懵,因为它听起来好像有点复杂,其实它的本质是考察你如何高效地处理数组中的逆序对,进而提升你的算法和编程思维。

这个问题的描述一般是这样的:

给定一个数组,你需要计算出该数组中有多少对(i, j),使得i < j 且nums[i] > nums[j]。

翻转对,这名字听起来挺酷的,不是吗?就像“翻转的世界”,指的是在你碰到的两个元素中,前面那个应该大,后面那个应该小,但实际情况是——它们恰恰相反。这种情况称为“逆序对”或者翻转对。

说实话,算法题中最让人头疼的就是这种“排好序,结果反了”的问题——每次你想着前面的数据应该大,后面的应该小,却发现它们互换了位置。

OK,那咱们先从最暴力的做法开始分析,然后一步步走向高效解法,毕竟作为一个程序员,做题嘛,最怕的就是笨方法。

暴力解法

暴力解法就是直接用两个嵌套的for循环,暴力检查每一对(i, j)。简单来说,就是逐个比较所有元素,找出符合条件的“逆序对”。

func countReversePairs(nums []int) int {
    count := 0
    for i := 0; i < len(nums); i++ {
        for j := i + 1; j < len(nums); j++ {
            if nums[i] > nums[j] {
                count++
            }
        }
    }
    return count
}

这里的思路很简单:nums[i]如果大于nums[j],那么这就是一个翻转对(逆序对)。但是,这个方法的时间复杂度是O(n^2),对于数据量大的情况,可能会很慢。毕竟,你得两两比较每一对,速度可想而知。

优化思路

接下来,我们要进行优化。暴力解法虽然直观,但显然不是最优解。为了提高效率,我们可以借助“归并排序”的思想来计算翻转对。

通过归并排序,我们可以在排序的过程中计算出逆序对的个数。归并排序本身是一个O(n log n)的算法,而在归并的过程中,我们可以同时统计逆序对的数量。

归并排序解法

当我们合并两个有序数组时,如果left[i] > right[j],那么说明left[i]和右侧数组right[j]及其后续元素都会形成翻转对。通过这种方式,我们可以在O(n log n)的时间复杂度内计算出翻转对的数量。

func countReversePairs(nums []int) int {
    return mergeSort(nums, 0len(nums)-1)
}

func mergeSort(nums []int, left, right int) int {
    if left >= right {
        return 0
    }

    mid := left + (right-left)/2
    count := mergeSort(nums, left, mid) + mergeSort(nums, mid+1, right)
    count += merge(nums, left, mid, right)

    return count
}

func merge(nums []int, left, mid, right int) int {
    temp := make([]int, right-left+1)
    i, j, k := left, mid+10
    count := 0

    // Count the reverse pairs during the merge step
    for i <= mid {
        for j <= right && nums[i] > nums[j] {
            count += (mid - i + 1// All elements from i to mid form a pair with nums[j]
            j++
        }
        i++
    }

    // Merge the two halves
    i, j = left, mid+1
    for i <= mid && j <= right {
        if nums[i] <= nums[j] {
            temp[k] = nums[i]
            i++
        } else {
            temp[k] = nums[j]
            j++
        }
        k++
    }

    // Copy the remaining elements
    for i <= mid {
        temp[k] = nums[i]
        i++
        k++
    }
    for j <= right {
        temp[k] = nums[j]
        j++
        k++
    }

    // Copy the merged array back to nums
    copy(nums[left:right+1], temp)

    return count
}

在这个解法中,mergeSort递归地将数组分成两部分,然后通过merge函数来计算逆序对的数量。在合并的过程中,我们每找到一个left[i] > right[j]的情况,就能直接统计出从left[i]left[mid]范围内的所有元素都与right[j]形成了翻转对。

效率分析

这个算法的时间复杂度是O(n log n),比暴力解法O(n^2)要高效得多。空间复杂度为O(n),因为我们使用了一个临时数组来存储合并的结果。对于大规模的数据,这个算法表现得非常不错。

对编程、职场感兴趣的同学,大家可以联系我微信:golang404,拉你进入“程序员交流群”。

🔥虎哥私藏精品 热门推荐🔥

虎哥作为一名老码农,整理了全网最全《GO后端开发资料合集》

资料包含了《IDEA视频教程》《最全GO面试题库》《最全项目实战源码及视频》《毕业设计系统源码》,总量高达650GB全部免费领取!全面满足各个阶段程序员的学习需求!

【声明】内容源于网络
0
0
Julia出海营销
跨境分享营 | 持续输出专业内容
内容 11733
粉丝 2
Julia出海营销 跨境分享营 | 持续输出专业内容
总阅读141.8k
粉丝2
内容11.7k