NC37 合并区间
描述
给出一组区间,请合并所有重叠的区间。
请保证合并后的区间按区间起点升序排列。
数据范围:区间组数 0 \le n \le 2 \times 10^50≤n≤2×105,区间内 的值都满足 0 \le val \le 2 \times 10^50≤val≤2×105
要求:空间复杂度 O(n)O(n),时间复杂度 O(nlogn)O(nlogn)
进阶:空间复杂度 O(val)O(val),时间复杂度O(val)O(val)
示例1
输入:
[[10,30],[20,60],[80,100],[150,180]]
返回值:
[[10,60],[80,100],[150,180]]
示例2
输入:
[[0,10],[10,20]]
返回值:
[[0,20]]
具体做法:
step 1:既然要求重叠后的区间按照起点位置升序排列,我们就将所有区间按照起点位置先进行排序。使用sort函数进行排序,重载比较方式为比较interval结构的start变量。
step 2:排序后的第一个区间一定是起点值最小的区间,我们将其计入返回数组res,然后遍历后续区间。
step 3:后续遍历过程中,如果遇到起点值小于res中最后一个区间的末尾值的情况,那一定是重叠,取二者最大末尾值更新res中最后一个区间即可。
step 4:如果遇到起点值大于res中最后一个区间的末尾值的情况,那一定没有重叠,后续也不会有这个末尾的重叠区间了,因为后面的起点只会更大,因此可以将它加入res。
方法一:
/*
* function Interval(a, b){
* this.start = a || 0;
* this.end = b || 0;
* }
*/
/**
*
* @param intervals Interval类一维数组
* @return Interval类一维数组
*/
function merge( intervals ) {
// 方法一
// 去除特殊情况
if(intervals.length<=1){
return intervals
}
// 按照首区间排序
intervals.sort((a,b)=>a.start-b.start);
let res=[];
// 放入第一个区间
res.push(intervals[0]);
for(let i=1;i<intervals.length;i++){
// 定义暂存区间
let int=new Interval();
int.start=intervals[i].start;
int.end=intervals[i].end;
// 如果区间无重叠,直接加入
if(res[res.length-1].end<int.start){
res.push(int)
// 区间有重叠更新结尾
}else if(res[res.length-1].end<=int.end){
res[res.length-1].end=int.end
}
}
return res
}
module.exports = {
merge : merge
};
方法二:
function merge( intervals ) {
// 方法二
if(intervals.length<=1){
return intervals
}
intervals.sort((a,b)=>a.start-b.start);
let res=[];
let len=intervals.length;
for(let i=0;i<len;i++){
let int =new Interval();
int.end=intervals[i].end;
int.start=intervals[i].start;
while(i<len-1&&int.end>=intervals[i+1].start){
int.end=Math.max(intervals[i+1].end,int.end)
i++;
}
res.push(int)
}
return res
}
module.exports = {
merge : merge
};
NC36 在两个长度相等的排序数组中找到上中位数
描述
给定两个递增数组arr1和arr2,已知两个数组的长度都为N,求两个数组中所有数的上中位数。
上中位数:假设递增序列长度为n,为第n/2个数
数据范围:1 \le n \le 10^51≤n≤105, 0 \le arr_1,arr_2 \le 10^90≤arr1,arr2≤109
要求:时间复杂度 O(n)O(n),空间复杂度 O(1)O(1)
进阶:时间复杂度为O(logN)O(log**N),空间复杂度为O(1)O(1)
示例1
输入:
[1,2,3,4],[3,4,5,6]
返回值:
3
说明:
总共有8个数,上中位数是第4小的数,所以返回3。
示例2
输入:
[0,1,2],[3,4,5]
返回值:
2
说明:
总共有6个数,那么上中位数是第3小的数,所以返回2
示例3
输入:
[1],[2]
返回值:
1
备注:
1 \leq N \leq 10^51≤N≤105
0 \leq arr_{1i}, arr_{2i} \leq 10^90≤arr1i,arr2i≤109
方法一:
/**
* find median in two sorted array
* @param arr1 int整型一维数组 the array1
* @param arr2 int整型一维数组 the array2
* @return int整型
*/
function findMedianinTwoSortedAray( arr1 , arr2 ) {
// 方法一
let arr=arr1.concat(arr2);
arr.sort((a,b)=>a-b);
let len=arr1.length+arr2.length;
return arr[len/2-1]
}
module.exports = {
findMedianinTwoSortedAray : findMedianinTwoSortedAray
};
方法二:
思路:
假设两个数组为一个数组,先算出上中位数的位置。
我们使用双指针去遍历两个数组,并且用一个数记录走过的次数,当次数到达上中位数则返回。
或者当有两个数组中的一个数组遍历完之后还没找到上中位数,这是可以确定上中位数在另外一个数组中,也可以根据已经走过的次数去定位到上中位数。
/**
* find median in two sorted array
* @param arr1 int整型一维数组 the array1
* @param arr2 int整型一维数组 the array2
* @return int整型
*/
function findMedianinTwoSortedAray( arr1 , arr2 ) {
// 方法二
let res=0, l=0,r=0,len=arr1.length;
while(len--){
if(arr1[l]>arr2[r]){
res=arr2[r++];
}else{
res=arr1[l++];
}
}
return res;
}
module.exports = {
findMedianinTwoSortedAray : findMedianinTwoSortedAray
};
NC8 二叉树中和为某一值的路径(二)
描述
输入一颗二叉树的根节点root和一个整数expectNumber,找出二叉树中结点值的和为expectNumber的所有路径。
1.该题路径定义为从树的根结点开始往下一直到叶子结点所经过的结点
2.叶子节点是指没有子节点的节点
3.路径只能从父节点到子节点,不能从子节点到父节点
4.总节点数目为n
如二叉树root为{10,5,12,4,7},expectNumber为22
则合法路径有[[10,5,7],[10,12]]
数据范围:
树中节点总数在范围 [0, 5000] 内
-1000 <= 节点值 <= 1000
-1000 <= expectNumber <= 1000
示例1
输入:
{10,5,12,4,7},22
返回值:
[[10,5,7],[10,12]]
说明:
返回[[10,12],[10,5,7]]也是对的
示例2
输入:
{10,5,12,4,7},15
返回值:
[]
示例3
输入:
{2,3},0
返回值:
[]
示例4
输入:
{1,3,4},7
返回值:
[]
/* function TreeNode(x) {
this.val = x;
this.left = null;
this.right = null;
} */
function FindPath(root, expectNumber)
{
let res=[];
let path=[];
function dfs(root,number){
// 处理树为空的情况
if(root==null)
return ;
// 更新路径
path.push(root.val);
// number更新
number-=root.val;
// 如果当前节点为叶子节点,且该路径的值已经达到指定数值,更新res
if(number==0&&root.left==null&&root.right==null){
res.push([...path])
}
// 左右子树递归
dfs(root.left,number);
dfs(root.right,number);
// 弹出节点
path.pop();
}
dfs(root,expectNumber);
return res;
}
module.exports = {
FindPath : FindPath
};
NC60 判断一棵二叉树是否为搜索二叉树和完全二叉树
描述
给定一棵二叉树,已知其中的节点没有重复值,请判断该二叉树是否为搜索二叉树和完全二叉树。
输出描述:分别输出是否为搜索二叉树、完全二叉树。
数据范围:二叉树节点数满足 0 \le n \le 5000000≤n≤500000 ,二叉树上的值满足 0 \le val \le 10^60≤val≤106
要求:空间复杂度 O(n)O(n),时间复杂度 O(n)O(n)
注意:空子树我们认为同时符合搜索二叉树和完全二叉树。
示例1
输入:
{2,1,3}
返回值:
[true,true]
示例2
输入:
{1,#,2}
返回值:
[true,false]
说明:
由于节点的右儿子大于根节点,无左子树,所以是搜索二叉树但不是完全二叉树
示例3
输入:
{}
返回值:
[true,true]
代码:
/*
* function TreeNode(x) {
* this.val = x;
* this.left = null;
* this.right = null;
* }
*/
/**
*
* @param root TreeNode类 the root
* @return bool布尔型一维数组
*/
function judgeIt( root ) {
// 方法一(中序遍历+层序遍历)
let res=[true,true];
let ins=[];
if(root==null){
return res;
}
// 中序遍历判断搜索二叉树
function inOrder(root){
if(root==null) return ;
inOrder(root.left);
ins.push(root.val);
inOrder(root.right);
}
inOrder(root);
for(let i=0;i<ins.length-1;i++){
if(ins[i+1]<ins[i]){
res[0]=false;
}
}
// 判断完全二叉树
function allTree(root){
let q=[];
let flag=false;
q.push(root);
while(q.length!=0){
let len=q.length;
let node=q.shift();
let left=node.left;
let right=node.right;
// flag=true说明之前的节点只有左节点或者没有节点,因此之后的节点还有子节点,说明该树不是完全二叉树
if(flag==true&&node.left!=null&&node.right!=null){
return false;
}
// 左右节点都存在
if(left!=null&&right!=null){
q.push(left);
q.push(right);
}
// 左节点不存在右节点,一定不是完全二叉树,直接返回false
else if(left==null&&right!=null){
return false;
// 左节点不为空,右节点为空,做标记,后面节点不能再有子节点
}else if(left!=null&&right==null){
flag=true;
q.push(left);
//
}else{
// 左右节点都为空,说明为叶子节点,做标记,后面的节点不能再有子节点
flag=true;
}
}
return true;
}
res[1]=allTree(root);
return res;
}
module.exports = {
judgeIt : judgeIt
};
NC24 删除有序链表中重复的元素-II
描述
给出一个升序排序的链表,删除链表中的所有重复出现的元素,只保留原链表中只出现一次的元素。
例如:
给出的链表为1 \to 2\to 3\to 3\to 4\to 4\to51→2→3→3→4→4→5, 返回1\to 2\to51→2→5.
给出的链表为1\to1 \to 1\to 2 \to 31→1→1→2→3, 返回2\to 32→3.
数据范围:链表长度 0 \le n \le 100000≤n≤10000,链表中的值满足 |val| \le 1000∣val∣≤1000
要求:空间复杂度 O(n)O(n),时间复杂度 O(n)O(n)
进阶:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)
示例1
输入:
{1,2,2}
返回值:
{1}
示例2
输入:
{}
返回值:
{}
方法一:
/*
* function ListNode(x){
* this.val = x;
* this.next = null;
* }
*/
/**
*
* @param head ListNode类
* @return ListNode类
*/
function deleteDuplicates( head ) {
// 方法一(利用map)
let map=new Map();
let p=head;
// 遍历链表,将链表中的节点存入map中
while(p){
if(map.has(p.val)){
let x=map.get(p.val)+1;
map.set(p.val,x);
}else{
map.set(p.val,1);
}
p=p.next;
}
// 新建链表
let list=new ListNode(null);
p=list;
// 遍历map
for(let [key,val] of map){
// 只有map中val为1的键才能组成新链表
if(val==1){
let node=new ListNode(key);
p.next=node;
p=p.next;
}
}
return list.next;
}
module.exports = {
deleteDuplicates : deleteDuplicates
};
方法二:
function deleteDuplicates( head ) {
// 方法二(遍历链表)
// 空链表或者表中只有一个节点
if(head==null||head.next==null){
return head;
}
let cur=new ListNode(null);
// 链表前加一个表头
cur.next=head;
let p=cur;
while(p.next&&p.next.next){
// 遇到相邻两个节点值相同
if(p.next.val==p.next.next.val){
// 保存当前值
let temp=p.next.val;
// 跳过后方与当前值相同的节点
while(p.next&&p.next.val==temp){
p.next=p.next.next;
}
}else{
p=p.next;
}
}
return cur.next;
}
module.exports = {
deleteDuplicates : deleteDuplicates
};