前提说明:本次的解题方法是归并排序,归并排序就是通过递归不断地将数组分成两个数组,然后将两个数组第一个元素做对比,谁小就将谁插入新的数组达到排序作用,然后将原数组中的相对应的位置替换,代码如下:
let countResult = function(arr,l,r){
//当l >= r 此时说明不存在或者只有一个节点
if(l >= r) return;
//获取中间索引将数组一分为二
let m = parseInt((l + r)/2);
//左边数组
countResult(arr,l,m);
//右边数组
countResult(arr,m + 1,r);
//下面开始数组合并
let k = l, p1 = l, p2 = m + 1,temp = [];
//p1是左边数组其实位置,m是左边数组终止位置,p2是右边数组起始位置,r是右边数组终止位置
while(p1 <= m || p2 <= r){
//当右边数组为空,或者左边数组存在且左边第一个节点小于右边第一个节点,那么就将左边第一个数组加入临时数组,否则就加入右边数组第一个
if(p2 > r || (p1 <= m && arr[p1] <= arr[p2])){
temp[k ++] = arr[p1 ++];
} else {
temp[k ++] = arr[p2 ++];
}
}
//将原数组中排序过的数组替换
for(let i = l; i <= r; i ++) arr[i] = temp[i];
return arr;
}
剑指 Offer 51. 数组中的逆序对
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:
输入: [7,5,6,4]
输出: 5
解题思路:本题就是通过归并排序来实现的,就是通过判断右边数组是否可以插入临时数组,如果右边数组能加入临时数组说明此时左边剩下的元素都是大于右边的,代码如下:
ar reversePairs = function(nums) {
//判断边界条件
if(nums.length < 1) return nums;
return countResult(nums,0,nums.length - 1);
};
let countResult = function(arr,l,r){
//当数组不存在或者只有一个的时候返回0
if(l >= r) return 0;
let m = parseInt((l + r)/2);
let ans = 0;
//累计左边有多少个逆序对
ans += countResult(arr,l,m);
//判断右边有多少个逆序对
ans += countResult(arr,m + 1,r);
let k = l, p1 = l, p2 = m + 1,temp = [];
//重新组合排序
while(p1 <= m || p2 <= r){
if(p2 > r || (p1 <= m && arr[p1] <= arr[p2])){
temp[k ++] = arr[p1 ++];
} else {
temp[k ++] = arr[p2 ++];
//此时说明左边剩下鄂数组元素都大于当前元素
ans += m - p1 + 1;
}
}
//替换已排序元素,实现归并
for(let i = l; i <= r; i ++) arr[i] = temp[i];
return ans;
}
23. 合并K个升序链表
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。 示例 1:
输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
1->4->5,
1->3->4,
2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6
示例 2:
输入: lists = []
输出: []
示例 3:
输入: lists = [[]]
输出: []
解题思路:这题用的是归并排序中的归,因为是有序的升序链表,难得是链表的处理,因为是链表的内容是有序的,所以本题用到了小顶堆,代码如下:
var mergeKLists = function(lists) {
//边界条件判断
if(lists == []) return lists;
//保存堆的数组
let data = [];
//获取堆的方法
let h = heap;
//将数组中的链表按照小顶堆排序
for(let i = 0; i < lists.length; i ++){
while(lists[i]){
data = h.push_up(lists[i],data,'min');
//获得当前数组链表的下一个节点
lists[i] = lists[i].next;
}
}
let t = new ListNode(0);
let p = t;
//合并链表
while(data.length > 0){
let cur = new ListNode(0);
cur = data[0];
//不这样操作会报错
cur.next = null;
p.next = cur;
p = cur;
data = h.pop_down(data,'min')
}
return t.next;
};
//type = max 就是大顶堆,min就是小顶堆
let heap = {
push_up(val,data,type){
data.push(val);
let idx = data.length - 1;//当前节点
let gIdx = parseInt((idx - 1) / 2);//根节点
while(idx && this.CMP(idx,gIdx,data,type)){
data = this.swap(data[idx],idx,data[gIdx],gIdx,data);
idx = gIdx;
gIdx = parseInt((idx - 1)/2);
}
return data;
},
pop_down(data,type){
if(data.length <= 1){
data = [];
return data;
}
let cnt = data.length;
//删除最大值,将尾结点放在开头,位置树结构
data[0] = data.pop();
cnt --;
let idx = 0;//当前节点
let n = cnt -1;//最大节点个数
let zIdx = 2 * idx + 1;//左子节点
while(zIdx <= n){
let temp = idx;//三角区最大值下标
if(this.CMP(zIdx,idx,data,type)) temp = zIdx;
if(zIdx + 1 <= n && this.CMP(zIdx + 1,temp,data,type)) temp = zIdx + 1;
if(temp == idx) break;
data = this.swap(data[idx],idx,data[temp],temp,data);
idx = temp;
zIdx = 2*idx +1;
}
return data;
},
swap(v1,i1,v2,i2,data){
data[i1] = v2;
data[i2] = v1;
return data;
},
CMP(val,val2,data,type){
if(type == 'max'){
if(data[val].val > data[val2].val) return true;
else return false
}else{
if(data[val].val < data[val2].val) return true;
else return false
}
}
}