算法训练营-排序

原地快排模板

/**
* @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 @   凌歆  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示