每日算法23_2020-11-29

来源:题库 - 力扣 (LeetCode) (leetcode-cn.com)。侵删。

题目一

976. 三角形的最大周长

给定由一些正数(代表长度)组成的数组 A,返回由其中三个长度组成的、面积不为零的三角形的最大周长。

如果不能形成任何面积不为零的三角形,返回 0。

示例 1:

输入:[2,1,2]
输出:5
示例 2:

输入:[1,2,1]
输出:0
示例 3:

输入:[3,2,3,4]
输出:10
示例 4:

输入:[3,6,2,3]
输出:8

提示:

3 <= A.length <= 10000
1 <= A[i] <= 10^6

我的答案

class Solution {
    public int largestPerimeter(int[] A) {
        // 两边之和大于第三边
        Arrays.sort(A);
        for (int i = A.length-1; i >= 2; i--) {
            if(A[i] < 2*A[i-1]) {
                for (int j = i-1; j >= 1; j--) {
                    if(A[j] <= 2*A[j-1] && A[i] < A[j] + A[j-1]){
                        return A[i] + A[j] + A[j-1];
                    }
                }
            }
        }

        return 0;
    }
}

12ms击败了11%,判断的太多了 。

其实精简一下就是这样

这也是官方答案 贪心 + 排序

class Solution {
    public int largestPerimeter(int[] A) {
		Arrays.sort(A);
        if (A.length<2){
            return 0;
        }
        for(int i=A.length-1;i>=2;i--) {
            if (A[i] < A[i-1] + A[i-2]) {
                return A[i] + A[i-1] + A[i-2];
            }
        }
        return 0;
	}
}

复杂度分析

  • 时间复杂度:O*(Nlog*N),其中 N是数组 A 的长度。
  • 空间复杂度:O(1)。

为什么排序遍历相邻元素可行,有没有可能最优解为非相邻元素?(不会)

证明:反证 假设 a , b, c 为最优解,且存在a’,b’,满足 a < a’ < b < b’ < c(存在非相邻元素)

  1. 由于 a + b > c,a < a’, 有 a’ + b > c,(a’, b, c)优于(a, b, c),与(a, b, c)为最优解矛盾,故不存在a’
  2. b’同理不存在 由于 a + b > c, b < b’,有a + b’ > c,(a, b, c)为最优解矛盾,故不存在b’

因此最优解一定为排序后相邻元素。

该证明来自于https://leetcode-cn.com/problems/largest-perimeter-triangle/comments/216024。

1ms的答案

class Solution {
    public int largestPerimeter(int[] A) {
        /*
        Arrays.sort(A); //默认升序排序

        //三角形三边满足a+b>c
        //找到c>b>a三个数,满足上述条件
        //若相邻的abc不能组成三角形,那么c之后的边更小,更不可能组成三角形,所以只需要判断相邻的三个数即可
        for(int i = A.length - 1; i > 1; i--){
            if(A[i] < A[i - 1] + A[i - 2])
                return A[i] + A[i - 1] + A[i - 2];
        }
        return 0;
        */
        
        int a = getMax(A);
        int b = getMax(A);
        int c = getMax(A);

        for(int i = 0; i < A.length - 2; i++){
            if(isTriangle(a, b, c)){
                return a + b + c;
            }
            a = b;
            b = c;
            c = getMax(A);
        }
        return 0;
    }

    public static int getMax(int A[]){
        int max = -1;
        int position = -1;
        for(int i = 0; i < A.length; i++){
            if(max < A[i]){
                max = A[i];
                position = i;
            }
        }
        if(position != -1){
            A[position] = -1;
        }
        
        return max;
    }

    public static boolean isTriangle(int a, int b, int c){
        return a < (b + c) && b < (a + c) && c < (a + b);
    }
}

题目二

5560. 设计前中后队列

请你设计一个队列,支持在前,中,后三个位置的 push 和 pop 操作。

请你完成 FrontMiddleBack 类:

FrontMiddleBack() 初始化队列。
void pushFront(int val) 将 val 添加到队列的 最前面 。
void pushMiddle(int val) 将 val 添加到队列的 正中间 。
void pushBack(int val) 将 val 添加到队里的 最后面 。
int popFront() 将 最前面 的元素从队列中删除并返回值,如果删除之前队列为空,那么返回 -1 。
int popMiddle() 将 正中间 的元素从队列中删除并返回值,如果删除之前队列为空,那么返回 -1 。
int popBack() 将 最后面 的元素从队列中删除并返回值,如果删除之前队列为空,那么返回 -1 。
请注意当有 两个 中间位置的时候,选择靠前面的位置进行操作。比方说:

将 6 添加到 [1, 2, 3, 4, 5] 的中间位置,结果数组为 [1, 2, 6, 3, 4, 5] 。
从 [1, 2, 3, 4, 5, 6] 的中间位置弹出元素,返回 3 ,数组变为 [1, 2, 4, 5, 6] 。

示例 1:

输入:
[“FrontMiddleBackQueue”, “pushFront”, “pushBack”, “pushMiddle”, “pushMiddle”, “popFront”, “popMiddle”, “popMiddle”, “popBack”, “popFront”]
[[], [1], [2], [3], [4], [], [], [], [], []]
输出:
[null, null, null, null, null, 1, 3, 4, 2, -1]

解释:
FrontMiddleBackQueue q = new FrontMiddleBackQueue();
q.pushFront(1); // [1]
q.pushBack(2); // [1, 2]
q.pushMiddle(3); // [1, 3, 2]
q.pushMiddle(4); // [1, 4, 3, 2]
q.popFront(); // 返回 1 -> [4, 3, 2]
q.popMiddle(); // 返回 3 -> [4, 2]
q.popMiddle(); // 返回 4 -> [2]
q.popBack(); // 返回 2 -> []
q.popFront(); // 返回 -1 -> [] (队列为空)

提示:

1 <= val <= 109
最多调用 1000 次 pushFront, pushMiddle, pushBack, popFront, popMiddle 和 popBack 。

我的答案

自定义双向链表。

class ListNode{
    int val;
    ListNode last;
    ListNode next;
    public ListNode(){};
    public ListNode(int val){this.val = val;};
}
class FrontMiddleBackQueue {

    ListNode top; // 首部的哑节点
    ListNode tail; // 尾部的哑节点
    ListNode mid; // 用于记录中间节点位置
    int length; // 记录双向链表长度

    public FrontMiddleBackQueue() {
        // 创建首尾的哑节点,并连接在一起
        top = new ListNode(0);
        tail = new ListNode(0);
        top.next = tail;
        tail.last = top;

        length = 0;
    }

    public void pushFront(int val) {
        // 将val添加到队列最前面
        ListNode node = new ListNode(val);
        // 先该节点的next指向双向链变的第一个节点top.next
        // 再双向链变的第一个节点top.next的last指向该节点
        node.next = top.next;
        top.next.last = node;
        // 该节点与首节点连接在一起
        node.last = top;
        top.next = node;

        length++;

        // 修改mid节点的位置
        if(length == 1){
            // 说明时第一个节点,那么mid就是它
            mid = node;
        }else if(length % 2 == 0){
            // 那么mid往前移一位
            mid = mid.last;
        }
        // System.out.println("pushFront:" + length);
    }

    public void pushMiddle(int val) {
        if(length == 0){
            pushFront(val);
        }else{
            ListNode node = new ListNode(val);

           if(length % 2 == 0){
                // 偶数放mid后面
                node.next = mid.next;
                mid.next.last = node;
                mid.next = node;
                node.last = mid;
            }else{
                // 奇数放mid前面
                node.last = mid.last;
                mid.last.next = node;
                node.next = mid;
                mid.last = node;
            }


            length++;

            if(length % 2 == 0){
                mid = mid.last;
            }else{
                mid = mid.next;
            }
            // System.out.println("pushMiddle:" + length);
        }
    }

    public void pushBack(int val) {
        ListNode node = new ListNode(val);
        // 该节点的last先指向双向链表的最后一个节点tail.last
        // 双向链表的最后一个节点tail.last的next再指向该节点
        node.last = tail.last;
        tail.last.next = node;
        // 该节点再与尾节点连接在一起
        node.next = tail;
        tail.last = node;

        length++;

        // 修改mid节点的位置
        if(length == 1){
            // 说明时第一个节点,那么mid就是它
            mid = node;
        }else if(length % 2 != 0){
            // 那么mid往前移一位
            mid = mid.next;
        }
        // System.out.println("pushBack:" + length);
    }

    public int popFront() {
        int ret = -1;
        if(length != 0){
            // 当双向链表不为空时
            // 删除双向链表的第一个节点,只要让首节点与第二个节点连接在一起即可
            ret = top.next.val; // 记录返回值
            top.next.next.last = top;
            top.next = top.next.next;

            length--;

            if(length == 0){
                mid = null;
            }else if(length % 2 != 0){
                mid = mid.next;
            }
            // System.out.println("popFront:" + length);
        }

        return ret;
    }

    public int popMiddle() {
        int ret = -1;
        if(length != 0){
            ret = mid.val;
            mid.next.last = mid.last;
            mid.last.next = mid.next;

            length--;

            if(length % 2 == 0){
                mid = mid.last;
            }else{
                mid = mid.next;
            }
            // System.out.println("popMiddle:" + length);
        }

        return ret;
    }

    public int popBack() {
        int ret = -1;
        if(length != 0){
            // 当双向链表不为空时
            // 删除双向链表的最后一个节点,只需要让尾节点与倒数第二个节点连接在一起即可
            ret = tail.last.val;
            tail.last.last.next = tail;
            tail.last = tail.last.last;

            length--;

            if(length == 0){
                mid = null;
            }else if(length % 2 == 0){
                mid = mid.last;
            }
            // System.out.println("popBack:" + length);
        }

        return ret;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值