题目一
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(存在非相邻元素)
- 由于 a + b > c,a < a’, 有 a’ + b > c,(a’, b, c)优于(a, b, c),与(a, b, c)为最优解矛盾,故不存在a’
- 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;
}
}