一、函数的独占时间
给你一个进程数量,和运行日志。运行日志log的格式为:进程id:(start/end):运行时间
其中一个进程运行时可以被另一个优先级较高的进程抢占cpu。求每个进程独占cpu的时间。
输入:n = 2, logs = ["0:start:0","1:start:2","1:end:5","0:end:6"] 输出:[3,4]
思路:
利用栈的特点(栈反应了各个进程使用cpu的情况);
1.当遇到start的时候,可能是新的进程优先级更高,也可能是此时没有进程使用cpu。
1.1 如果是优先级更高的进程抢占了,那么栈一定不为空,更新之前进程的独立运行时间
1.2 如果栈为空,就说明之前没有进程使用cpu 那么直接入栈 更新startTime
2. 遇到end就结束了,更新当前进程的独立运行时间。
代码:
class Solution {public int[] exclusiveTime(int n, List<String> logs) {int[] time=new int[n];Stack<Integer> stack=new Stack<>();int startTime=-1;for(String log:logs){String[] split=log.split(":");int id=Integer.parseInt(split[0]);int curTime=Integer.parseInt(split[2]);if(split[1].equals("start")){
//栈不为空 说明是优先级更高的进程抢占了,需要更新之前进程独立运行的时间if(!stack.isEmpty())time[stack.peek()]+=curTime-startTime;stack.push(id);//入栈startTime=curTime;}else{//如果遇到了"end"符 就说明该进程结束了time[id]+=curTime-startTime+1;stack.pop();startTime=curTime+1;}}return time;}
}
链表
二、删除链表的倒数第N个节点(双指针)二刷
给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。
思路:
定义slow、fast指针。如何让slow指针移动到要删除节点的前一个节点。eg:如上图所示,先让fast移动n下,也就是先把5,4移动了。然后同时移动1 2 3 。这时fast下一个必为空。拿这个当作判断条件,就可以把slow移动到删除结点的前一个结点
class Solution {public ListNode removeNthFromEnd(ListNode head, int n) {ListNode dummyHead=new ListNode();dummyHead.next=head;//设置虚拟头节点ListNode fast=dummyHead;ListNode slow=dummyHead;for(int i=0;i<n;i++)fast=fast.next;while(fast.next!=null){fast=fast.next;slow=slow.next;}slow.next=slow.next.next;return dummyHead.next;}
}
三、扁平化多级双向链表(类似于插入节点)
你会得到一个双链表,其中包含的节点有一个下一个指针、一个前一个指针和一个额外的 子指针 。给定链表的头节点 head ,将链表 扁平化 ,以便所有节点都出现在单层双链表中。
题意:
跟插入节点类似,但插入节点一般是插入单个节点。这个要插入多个节点。
如果有子节点的话,下一个节点就是子节点,如果没有子节点,下一个节点就是next。
思路:
1.判断是否有子节点,如果没有,下一个节点就是next
2.如果有子节点,下一个节点就是如何规则的孩子节点们...,孩子节点们的下一个节点是该节点之前的下一个节点。
if(head.child!=null)Node temp=head.next;Node child=flatten(head.child);head.next=child;child.prev=head;head.child=null;
while(child.next!=null)child=child.next; child.next=temp; if(temp!=null)temp.prev=child;
代码:
class Solution {public Node flatten(Node head) {Node dummyHead =new Node();dummyHead.next=head;while(head!=null){if(head.child!=null){//存储head节点之前的下一个节点Node temp=head.next;//获取孩子节点Node child=flatten(head.child);head.next=child;child.prev=head;head.child=null;//孩子节点变成下一个节点 因此孩子节点消失//将head节点之前的下一个节点放到孩子最后一个节点后面while(child.next!=null)child=child.next;child.next=temp;if(temp!=null)temp.prev=child;}else{head=head.next;}}return dummyHead.next; }
}
四、反转链表II(一次遍历穿针引线/双指针?)
给你单链表的头指针 head
和两个整数 left
和 right
,其中 left <= right
。请你反转从位置 left
到位置 right
的链表节点,返回 反转后的链表 。
一次遍历穿针引线法:
第一次:1->3->2->4->5
第二次:1->4->3->2->5
思路:
pre、cur、next三个指针。pre指针一直不变,next=cur.next;所以三个节点中只有一个cur是在主动变化。大概思路就是:每次先将cur.next=next.next,然后将next.next改为pre.next。也就是将这个节点提前。pre.next=next;
工作流程:
eg:1-2-3-4-5 left:2 right:4
1.pre=1 cur=2 next=3
2.首先将3从2的next中去除(因为3要提前),变成2-4-5
3.然后pre和next相连,变成1-3
4.next和cur相连 1-3-2-4-5
代码:
class Solution {public ListNode reverseBetween(ListNode head, int left, int right) {ListNode dummyHead=new ListNode();dummyHead.next=head;ListNode pre=dummyHead;for(int i=0;i<left-1;i++)pre=pre.next;//pre就是要第一个反转节点的前一个节点ListNode cur=pre.next;ListNode next=new ListNode();for(int i=0;i<right-left;i++){//每次更新next 随着cur的变化而变化next=cur.next;//先将next在cur.next中省去(因为next要提前)cur.next=next.next;//将next提前到至pre.nextpre.next=next;//连接next和curnext.next=cur;}return dummyHead.next;}
}
双指针翻转?
思路:
1.首先找到翻转部分第一个节点的前一个结点pre;并且找到翻转部分的最后一个结点rightNode
2.然后截取中间部分,ListNode leftNode=pre.next;ListNode curr=rightNode.next;
要反转的部分是:以leftNode为起点到以rightNode为结尾的这一部分。然后把两边部分的结点断开:pre.next=null;rightNode.next=null;
3.然后放到reverseLinkedList(leftNode)进行翻转
4.翻转之后,rightNode翻到前面,pre.next=rightNode; 然后leftNode翻到后面,leftNode.next=curr;
5.返回 return dummyHead.next;
代码:
class Solution {public ListNode reverseBetween(ListNode head, int left, int right) {ListNode dummyHead=new ListNode();dummyHead.next=head;ListNode pre=dummyHead;//第一步 找到prefor(int i=0;i<left-1;i++)pre=pre.next;//pre就是要第一个反转节点的前一个节点//第二步 找到反转范围内最右边的结点ListNode rightNode=dummyHead;//右边的节点for(int i=0;i<right;i++){rightNode=rightNode.next;}//第三步切断出一个子链表(截取链表)ListNode leftNode=pre.next;ListNode curr=rightNode.next;pre.next=null;rightNode.next=null;//反转 以leftNode为起点 以rightNode为结尾的这段范围中的reverse(leftNode);//拼接pre.next=rightNode;leftNode.next=curr;return dummyHead.next;}public void reverse(ListNode head){ListNode dummyHead =new ListNode();dummyHead.next=head;ListNode pre=dummyHead;ListNode cur=head;while(cur!=null){ListNode temp=cur.next;cur.next=pre;pre=cur;cur=temp;}}
}
五、旋转链表
给你一个链表的头节点 head
,旋转链表,将链表每个节点向右移动 k
个位置。
思路:
找到翻转后新的头节点,然后将新头节点之前的结点都放到新节点中最后一个结点之后。
首先要计算出链表的长度length,然后计算出新头节点前一个的位置index;index=length-(k%length)。如果从虚拟头结点开始数的话,这个位置应该是前一个结点的位置。
然后把前面那些结点连到后面就行了。
代码:
class Solution {public ListNode rotateRight(ListNode head, int k) {if(head==null||head.next==null||k==0)return head;ListNode dummyHead=new ListNode();dummyHead.next=head;//计算链表的长度ListNode cur=dummyHead;int length=0;while(cur!=null&&cur.next!=null){cur=cur.next;length++;}if(k%length==0)return head;//找到旋转后的首结点 length-(k%length)+1;ListNode pre=dummyHead;//旋转首节点的前一个结点int index=length-(k%length);//示例1中4的位置for(int i=0;i<index;i++){pre=pre.next;}ListNode realHead=pre.next;pre.next=null;ListNode rightNode=realHead;while(rightNode.next!=null){rightNode=rightNode.next;}rightNode.next=dummyHead.next;dummyHead.next=realHead;return dummyHead.next;}
}
六、两数相加II
思路:
和两数相加I类似,1.先把两个链表反转 2.然后相加 3.最后将结果反转返回
代码:
class Solution {public ListNode addTwoNumbers(ListNode l1, ListNode l2) {ListNode dummyHead1=new ListNode();dummyHead1.next=reverseLinkedList(l1);ListNode addHead=dummyHead1;ListNode dummyHead2=new ListNode();dummyHead2.next=reverseLinkedList(l2);ListNode dummyHead3=new ListNode();ListNode cur=dummyHead3;int carry=0;while(dummyHead1.next!=null||dummyHead2.next!=null){int num1=dummyHead1.next==null?0:dummyHead1.next.val;int num2=dummyHead2.next==null?0:dummyHead2.next.val;System.out.println("num1:"+num1+" num2:"+num2);cur.next=new ListNode((num1+num2+carry)%10);cur=cur.next;carry=(num1+num2+carry)/10;if(dummyHead1.next!=null)dummyHead1=dummyHead1.next;if(dummyHead2.next!=null)dummyHead2=dummyHead2.next;}if(carry!=0)cur.next=new ListNode(carry);return reverseLinkedList(dummyHead3.next);}public ListNode reverseLinkedList(ListNode head){ListNode dummyHead=new ListNode();dummyHead.next=head;ListNode slow=null;ListNode fast=head;while(fast!=null){ListNode temp=fast.next;fast.next=slow;slow=fast;fast=temp;}return slow;}
}
七、合并k个升序链表
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
输入: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
思路:
在长度为n的结点数组中,遍历n次,找到最小的结点,然后放到新链表的下一个。并且更新最小结点为最小结点的下一个。
外层循环while(true),在内层循环for(int i=0;i<n;i++)中如果没有找到一个最小的节点(说明已经寻找完毕了,这时就要break;)
代码:
class Solution {public ListNode mergeKLists(ListNode[] lists) {int k=lists.length;ListNode dummyHead=new ListNode();ListNode cur=dummyHead;while(true){ListNode minNode=null;int minPointer=-1;for(int i=0;i<k;i++){if(lists[i]==null)continue;if(minNode==null||lists[i].val<minNode.val){minNode=lists[i];minPointer=i;}}if(minPointer==-1)break;//结束了 链表里面没有东西了cur.next=minNode;cur=cur.next;lists[minPointer]=lists[minPointer].next;}return dummyHead.next;}
}