算法训练营-排序

原地快排模板

/**
 * @param {number[]} nums
 * @return {number[]}
 */
 var sortArray = function(nums) {

    const partition=(arr)=>{
        // 生成0-arr.length-1的随机数
        let mid=Math.floor(Math.random()*arr.length)
        let midValue=arr[mid]

        let left=0
        let right=arr.length-1

        while(left<=right){
            while(arr[left]<midValue){
                left++
            }

            while(arr[right]>midValue){
                right--
            }

            if(left<=right){
                // 交换left和right下标对应的元素
                let temp=arr[left]
                arr[left]=arr[right]
                arr[right]=temp

                left++
                right--
            }
        }

        return right
    }

    // 原地快排

    const quickSort=(arr)=>{

        if(arr.length<=1) return arr

        let pivot=partition(arr)
        
        let leftArr=arr.slice(0,pivot+1)
        let rightArr=arr.slice(pivot+1,arr.length)

        let orderLeft=quickSort(leftArr)
        let orderRight=quickSort(rightArr)

        return [...orderLeft,...orderRight]
    }

    console.log( quickSort(nums))

    

};

sortArray([3,9,7,8,5,13,6])

计数排序模板

  • 适用于n较小的情况
/**
 * @param {number[]} nums
 * @return {number[]}
 */
var sortArray = function(nums) {

    // 基数排序

    let arr=[]

    for(let i=-50000;i<=50000;i++){
        arr[i]=0
    }

    for(let i=0;i<nums.length;i++){
        arr[nums[i]]++
    }

    let res=[]
    // let n=0
    for(let i=-50000;i<500001;i++){
        // if(arr[i]===undefined) continue
        while(arr[i]>0){
            res.push(i)
            arr[i]--
            // n++
        }
    }

    return res

};

1122. 数组的相对排序

给你两个数组,arr1arr2arr2 中的元素各不相同,arr2 中的每个元素都出现在 arr1 中。

arr1 中的元素进行排序,使 arr1 中项的相对顺序和 arr2 中的相对顺序相同。未在 arr2 中出现过的元素需要按照升序放在 arr1 的末尾。

示例 1:

输入:arr1 = [2,3,1,3,2,4,6,7,9,2,19], arr2 = [2,1,4,3,9,6]
输出:[2,2,2,1,4,3,3,9,6,7,19]

示例 2:

输入:arr1 = [28,6,22,8,44,17], arr2 = [22,28,8,6]
输出:[22,28,8,6,17,44]

提示:

  • 1 <= arr1.length, arr2.length <= 1000
  • 0 <= arr1[i], arr2[i] <= 1000
  • arr2 中的元素 arr2[i] 各不相同
  • arr2 中的每个元素 arr2[i] 都出现在 arr1
完整代码
/**
 * @param {number[]} arr1
 * @param {number[]} arr2
 * @return {number[]}
 */
var relativeSortArray = function(arr1, arr2) {

    // 对arr1进行计数排序

    // 遍历arr2中的数组,把有计数的输出,计数减为0

    // 再遍历arr,把没出现在arr2的也输出

    let arr=new Array(1001).fill(0)

    let res=[]
    arr1.forEach((item)=>{
        arr[item]++
    }) 

    arr2.forEach((item)=>{
        while(arr[item]>0){
            arr[item]--
            res.push(item)
        }
    })

    arr.forEach((item,index)=>{
        while(item>0){
            item--
            res.push(index)
        }
    })

    return res


};

56. 合并区间

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间

示例 1:

输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].

示例 2:

输入:intervals = [[1,4],[4,5]]
输出:[[1,5]]
解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。

提示:

  • 1 <= intervals.length <= 104
  • intervals[i].length == 2
  • 0 <= starti <= endi <= 104
解题思路
1. 状态空间暴力,区间没有访问过,开始往后连接,连接过的不能用,循环所有没有访问过的空间,当不能在向下连接时,输出结果

2. 双关键字排序,即区间l1<=l2 r1<=r2 ,排好序后,判断是否可以向后连接,不行就另起一段,行就放入答案

3. 差分数组
状态空间暴力
/**
 * @param {number[][]} intervals
 * @return {number[][]}
 */
var merge = function(intervals) {

    // 状态空间暴力

    // 先对intervals进行部分排序

    intervals.sort((a,b)=>{
        return a[0]-b[0]
    })


    let res=[]
    
    // 递归,合并或者不能合并,看能走多远
    let visited=new Array(intervals.length).fill(false)

    let mostDist=[]
    const dfs=(arr)=>{

        mostDist=arr
        
        for(let i=0;i<intervals.length;i++){

            if(visited[i]) continue
            // visited[i]=true
            // 判断是否能够合并
            if(intervals[i][0]>=arr[0]&&intervals[i][0]<=arr[1]){
                visited[i]=true
                // 合并
                let newArr=new Array()
                newArr[0]=Math.min(intervals[i][0],arr[0])
                newArr[1]=Math.max(intervals[i][1],arr[1])
                dfs(newArr)
                // console.log(newArr)
                // far
                if(mostDist[1]<newArr[1]){
                    mostDist[1]=newArr[1]
                }
                break
            }else{
                // 走到底了,不能在合了 
                break
            }

        } 

        return mostDist
    }


    
    for(let j=0;j<intervals.length;j++){
        if(!visited[j]){
           mostDist=[]
           visited[j]=true
           res.push(Array.from(dfs(intervals[j])))
        }
    }

    // console.log(res)
    return res

};
双关键字排序+判定
/**
 * @param {number[][]} intervals
 * @return {number[][]}
 */
var merge = function(intervals) {

    // 区间合并

    // 双关键字排序
    // 我们先把区间进行排序,l1<l2  l1=l2时  排序r1<r2
    intervals.sort((a,b)=>{
        return a[0]-b[0] || (a[0]===b[0]&&a[1]-b[1])
    }) 

    // console.log(intervals)

    // 遍历区间
    // 看最远能延续到哪里

    // [1,3]  3
    // [2,6]  6

    // 另起一段
    // [8,10] 10

    // 另起一段
    // [15,18] 18

    // 记录起点
    let left=-1 
    // 记录最远的距离
    let far=-1

    let res=[]
    intervals.forEach((item)=>{
        let start=item[0]
        let end=item[1]

        if(start<=far){
            // 可以延续
            far=Math.max(far,end)
        }else{
            // 不可延续,另起一段
            if(far>=0){
                res.push([left,far])
            }
            left=start
            far=end
        }
    })

    // 最后可能一直在连续,需要把最后一段加入
    if(far>=0){
        res.push([left,far])
    }

    return res

};
差分数组
/**
 * @param {number[][]} intervals
 * @return {number[][]}
 */
var merge = function(intervals) {

    // 使用计数排序的思想进行区间的合并

    // [1,3] 1 2 3

    // [2,6] 2,3,4,5,6

    // [8,10] 8,9,10

    // [15,18] 15,16,17,18

    // 优化版本

    // 使用差分数组

    // 1 2 3 4 5 6 7 8 9 10
    // 差分数组
    // 1 1 1 1 1 1 1 1 1 1
    
    // 我们覆盖[1,3],即1-3都加上1
    // 2 1 1 0
    // 实际上只需要在1 +1   在4处-1


    // 定义差分事件,+1 开始覆盖
    // -1  结束覆盖

    let m=[]

    // 定义开始覆盖下标到 事件的映射

    intervals.forEach((item)=>{
        m.push([item[0],1])
        m.push([item[1]+1,-1])
    })

    // 根据开始覆盖的事件在前进行排序
    m.sort((a,b)=>{
        return a[0]-b[0]||(a[0]===b[0]&&a[1]-b[1])
    })

    console.log(m)

    // 定义一个count值,如果count 0-1 说明开始覆盖

    // 1-0 结束覆盖,输出left-far
    let count=0

    let left=-1
    let far=-1
    let res=[]
    m.forEach((item)=>{
        if(count===0){
            left=item[0]
        }

        count+=item[1]

        if(count===0){
            far=item[0]-1
            res.push([left,far])
        }
    })

    return res
   

};

493. 翻转对

给定一个数组 nums ,如果 i < jnums[i] > 2*nums[j] 我们就将 (i, j) 称作一个*重要翻转对*

你需要返回给定数组中的重要翻转对的数量。

示例 1:

输入: [1,3,2,3,1]
输出: 2

示例 2:

输入: [2,4,3,5,1]
输出: 3

注意:

  1. 给定数组的长度不会超过50000
  2. 输入数组中的所有数字都在32位整数的表示范围内。
解题思路
1.  暴力,超时
2. 条件判断,这里满足不等式i<j nums[i]>2*nums[j], 但是下标和值的不等式矛盾,用二分不能兼顾两边
3. 一般遇到这边的,使用归并排序,减小规模,减低时间复杂度求解
4. 满足条件的情况,分为左边部分满足的,右边部分满足的,和
	跨左右部分满足的,因为递归的最后面(只存在左右跨部分满足的(关键))
    
5. 是否满足,双指针烧苗,i在左边,j在右边,限固定i,看j是否满足
6. 双指针的优化 nums[i]>2*nums[j]  ,是不是下标在i右边的,也满足,因为数组有序
完整代码
/**
 * @param {number[]} nums
 * @return {number}
 */
var reversePairs = function(nums) {

    // 满足两种不等式i<j
    // nus[i]>2*nums[j]

    // 这两种不等式发生了冲突

    // 如果不冲突,二分
    // 冲突了,考虑使用归并排序解决

    let ans=0

    const merge=(arr,left,mid,right)=>{

        // 临时数组
        let temp=new Array(right-left+1)

        let i=left
        let j=mid+1

        // 合并两个有序数组
        for(let k=0;k<temp.length;k++){
            
            if(j>right||arr[i]<=arr[j]&&i<=mid){
                temp[k]=arr[i++]
            }else{
                temp[k]=arr[j++]
            }
        }

        for(let k=0;k<temp.length;k++){
            arr[left+k]=temp[k]
        }
    }

    // 原地归并,不生产子数组

    const mergeSort=(arr,left,right)=>{
        if(left>=right) return
        let mid=Math.floor((left+right)/2)

        mergeSort(arr,left,mid)
        mergeSort(arr,mid+1,right)

        // 双指针扫描

        for(let i=left,j=mid;i<=mid;i++){
            while(j<right&&arr[i]>2*arr[j+1]){
                j++
            }

            ans+=(j-mid)
        }

        merge(arr,left,mid,right)
    }

    mergeSort(nums,0,nums.length-1)
    // console.log(sort)

    return ans
};
posted @ 2023-02-03 13:14  凌歆  阅读(21)  评论(0)    收藏  举报