算法学习day22

一、函数的独占时间

给你一个进程数量,和运行日志。运行日志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.next
            pre.next=next;
            //连接next和cur
            next.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;
        //第一步 找到pre
        for(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;
    }
}

  • 29
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值