Leetcode 849 题解

Leetcode 849 题解

前言

通过坚持以下的刷题思维,能够提高解决能力的问题:

  1. 搞清楚,题目是什么。比方说,需要搞懂当前的题目输入是什么,要求的结果是什么,限制是什么。这一步需要了解题目的含义和限制
  2. 验证和搞清楚接这个题目的思路是什么。比方说,需要收集和记录哪些信息,来得到解决方案要的信息,最后把信息,得到最后的答案。数学之美的吴军老师说过,信息是独立于语言的,解法就是信息的一种,这一步,需要搞清楚的是,优美表现出信息。
  3. 用编程语言的数据结构来实现想法,我们需要用自己熟悉的语言实现上面的解法,如何使解法优美的表达出来。比方说,同样是和你女朋友说我爱你,可以说 “我爱你”, “我养你啊”,“你还不来我怎么敢老去”。这一步中,需要做的是优美表示出想法。
  4. 了解实现的优缺点,我们需要提炼所实现的有哪些有点和那些缺点。

下面通过上面这四点,来理解如何解决这一道题目.

一、题目是什么?

  1. 到最近的人的最大距离

给你一个数组 seats 表示一排座位,其中 seats[i] = 1 代表有人坐在第 i 个座位上,seats[i] = 0 代表座位 i 上是空的(下标从 0 开始)。

至少有一个空座位,且至少有一人已经坐在座位上。

例子1 : ( 两边都有人的情况 )
输入:seats = [1,0,0,0,1,0,1]
输出:2
解释:
如果亚历克斯坐在第二个空位(seats[2])上,他到离他最近的人的距离为 2 。如果亚历克斯坐在其它任何一个空位上,他到离他最近的人的距离为 1 。 因此,他到离他最近的人的最大距离是 2 。

例子2: 只有一边有人,
输入:seats = [1,0,0,0]
输出:3
解释: 如果亚历克斯坐在最后一个座位上,他离最近的人有 3 个座位远。 这是可能的最大距离,所以答案是 3 。

例子3: 只有一边有人
输入:seats = [0,1]
输出:1

题目限制:
提示: 2 <= seats.length <= 2 * 10^4
seats[i] 为 0 或 1
至少有一个 空座位 (一定可以落座, 所以不用处理这种特殊情况)
至少有一个座位上有人 (不存在整个都为空的可情况,可以不处理这种特殊情况)

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximize-distance-to-closest-person

根据例子, 当中可以给出以下几种情况:

  1. 需要落座的地方, 是两边都有人的, 比方说
    seats = [1,0,0,0,1,0,1]
    这时候,计算的最近的人的最大距离 就是 长度 的 一半
    即:(4-0)/ 2 = 2

  2. 只有一边坐人的特殊情况, 比方说
    seats = [1,0,0,0], 到末尾都没有人坐
    这时候,计算的最近的人的最大距离 就是 长度
    即: (3-0)
    seats = [0,0,0,1], 从开头开始都没有人坐
    答案同样也是 (3-0)

二、描述解题想法

知道题目中有两种情况(普通情况和特殊情况),我们需要的信息分别是

  1. 在两人中间落座,需要知道两人的距离, 即: seats = [1,0,0,0,1,0,1], 图中两个红色的1的距离。
  2. 只有一个人落座, 需要第一个落座的人,或者最后一个落座的人的距离,即:seats = [1,0,0,0] 和 seats = [0,0,0,1] 中红色的1 的位置

我们可以通过一次遍历:来求得上面的信息, 并且得到最终的答案。
解题过程,可以在思维中得到以下的过程:
输入:seats = [0,0,0,1,0,0,0,1,0,1,0,0]
过程:
把上面的信息重新整理为,最后添加了一个位置,标记为 4
seats = [0,0,0,1,0,0,0,2,0,3,0,0,4]
当到达 1 的位置的时候,可以知道这是特殊情况,长度为 len = 3 - 0 = 3
当到达 2 的位置的时候,可以知道这是普通情况,长度为 len = (7 - 3)/2 = 2.
当到达 3 的位置的时候,可以知道这是普通情况,长度为 len = (9 - 7)/2 = 1.
当到达 4 的位置的时候,可以知道这是特殊情况,长度为 len = 11 - 9 - 1 = 2

因为到达4的时候,求法中需要多一个减 1,我们还需要调整一下过程,调整初始位:

seats = [-1,0,0,0,1,0,0,0,2,0,3,0,0,4]
当到达 1 的位置的时候,可以知道这是特殊情况,长度为 len = 3 - (-1) - 1 = 3
当到达 2 的位置的时候,可以知道这是普通情况,长度为 len = (7 - 3)/2 = 2.
当到达 3 的位置的时候,可以知道这是普通情况,长度为 len = (9 - 7)/2 = 1.
当到达 4 的位置的时候,可以知道这是特殊情况,长度为 len = 12 - 9 - 1 = 2

大概的推理过程如上, 在这里,大概能了解,需要一个遍历的过程,编辑记录上面的距离信息。下面,就需要不断用编程语言来实现并且验证上面的想法有什么错误之处。

实际中,形成上面的想法,不是一蹴而就,而是不断磨合出来的。

三、编程语言和数据结构实现想法

一般实现: 一个循环实现思想,但时间为16ms

下面的代码实现了上面的想法,通过一个循环 中判断,是否为特殊情况,然后进行特殊的长度计算。
如果是普通情况,就进行普通的长度的计算。
最后通过res 保存最长的距离,最后返回距离。
代码实现如下:

class Solution {
public:
  int maxDistToClosest(vector<int>& seats) {
    int res = 0;
    int last = -1, cur = 0;
    int n_seat = seats.size();
    int len;
    while (cur <= n_seat) {
      if (seats[cur] || cur == n_seat) {
        if (last == -1 || cur == n_seat) {
          len = cur - last - 1;
        }
        else {
          len = ((cur - last) >> 1);
        }
        res = max(res, len);
        last = cur;
      }
      ++cur;
    }
    return res;
  }
};

最终得到的实测结果是:
1
通过查看提交记录的别的答案,比实现中更好的速度的代码例子:
比如说下面这一个

class Solution {
public:
    int maxDistToClosest(vector<int>& seats) {
        int count1 = 0;
        int count2 = 0;
        int i = 0, j = seats.size()-1;
        while (i < seats.size() && seats[i] == 0) {
            count1++;
            i++;
        }
        while (j >= 0 && seats[j] == 0) {
            count2++;
            j--;
        }
        int distance = 0;
        int res = 0;
        for (int k = i + 1; k <= j; ++k) {
            if (seats[k]==0) {
                res++;
            } else {
                distance = max(distance, res);
                res = 0;
            }
        }

        return max(max(count1, count2), (distance + 1) / 2);
    }
};

实际测试结果如下:
2

优化实现: 多个循环语句减少if语句, 达到12ms

通过分析代码,主要是针对特殊情况,进行的分别的处理,而让循环当中,不要用过多的 if 进行判断。
根据这个信息,再次优化实现的代码,可以得到:

class Solution {
public:
  int maxDistToClosest(vector<int>& seats) {
    int res = 0;
    int last = -1, cur = 0;
    int n_seat = seats.size();
    // 处理特殊情况
    while (!seats[cur]) ++cur;
    res = max(res, cur - last - 1);
    last = cur;
    // 处理普通情况
    while (cur < n_seat) {
      if (seats[cur]) {
        res = max(res, ((cur - last) >> 1));
        last = cur;
      }
      ++cur;
    }
    // 处理特殊情况
    res = max(res, n_seat - last - 1);
    return res;
  }
};

实测的结果如下,能够有 12 ms 的结果了,而且代码更简洁,少了运用了一个 while。
3
最终实现和最优的代码一样的时间, 并且代码也更加优美好看.

四、实现的优缺点

这种想法是通过一次遍历,就能够通过得到所需要的答案所以是 O(N)的复杂度, N是数组的长度。
在我们的时间当中,时间复杂也是 O(N),空间复杂度是 O(1)。
但是在不同的实现当中,会有细微的差别,在每一次的操作当中,if 的判断次数会有不同。所以,及时是O(N)时间的复杂度也还会有不同。

五、本地调试CPP代码

// test_judge.h 
#include "test_header.h"


template<typename ANS_T>
bool judge(ANS_T &your_ans, ANS_T &right_ans)
{
  return your_ans == right_ans;
}

bool judge(vector<int> &your_ans, vector<int> &right_ans)
{
  if (your_ans.size() != right_ans.size()) return false;
  for (int i = 0; i < your_ans.size(); ++i)
  {
    if (your_ans[i] != right_ans[i]) return false;
  }  
  return true;
}

template<>
bool judge(vector<vector<int>> &your_ans, vector<vector<int>> &right_ans)
{
  if (your_ans.size() != right_ans.size()) return false;
  for (int i = 0; i < your_ans.size(); ++i)
  {
    if (your_ans[i].size() != right_ans[i].size()) return false;
    for (int j = 0; j < your_ans.size(); ++j)
    {
      if (your_ans[i][j] != right_ans[i][j]) return false;
    }    
  }  
  return true;
}
// test_debug.h```
#include "test_header.h"

struct ListNode {
  int val;
  ListNode *next;
  ListNode() : val(0), next(nullptr) {}
  ListNode(int x) : val(x), next(nullptr) {}
  ListNode(int x, ListNode *next) : val(x), next(next) {}
 };

ostream& operator<<(ostream& os, const vector<string>& ans)
{
  for (int i = 0; i < ans.size(); ++i) {
    os << " " << ans[i];
  }
  os << endl;
  return os;
}

ostream& operator<<(ostream& os, ListNode* ans)
{
  while (ans) {
    os << " " << ans->val;
    ans = ans->next;
  }
  os << endl;
  return os;
}

ostream& operator<<(ostream& os, const vector<int>& ans)
{
  for (int i = 0; i < ans.size(); ++i) {
    os << " " << ans[i];
  }
  os << endl;
  return os;
}

ostream& operator<<(ostream& os, const vector<string>& ans)
{
  for (int i = 0; i < ans.size(); ++i) {
    os << " " << ans[i];
  }
  os << endl;
  return os;
}

ostream& operator<<(ostream& os, const vector<vector<string>>& ans)
{
  os << endl;
  for (int i = 0; i < ans.size(); ++i) {
    os << " " << ans[i];
  }
  os << endl;
  return os;
}

ostream& operator<<(ostream& os, const vector<vector<int>>& ans)
{
  os << endl;
  cout << "vector stars: " << endl;
  for (int i = 0; i < ans.size(); ++i) {
    for (int j = 0; j < ans[i].size(); ++j) {
      os << " " << ans[i][j];
    }
    cout << endl;
  }
  cout << "vector ends: " << endl;
  return os;
}

void printVecVec(const int *ans[], int m, int n) {
  cout << endl;
  cout << "vector stars: " << endl;
  for (int i = 0; i < m; ++i) {
    for (int j = 0; j < n; ++j) {
      cout << " " << ans[i][j];
    }
    cout << endl;
  }
  cout << "vector ends: " << endl;
}
```cpp
// test_header.h
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <climits>
#include <cmath>
#include <string>
#include <stack>
#include <map>
#include <unordered_map>
#include <climits>
#include <limits.h>
#include <bitset>
#include <queue>
#include <random>
#include <tuple>
#include <set>
#include <unordered_set>
#include <algorithm>

using namespace std;
// 849.cc
#include "test_header.h"
#include "test_output.h"
#include "test_judge.h"

class Solution {
public:
  int maxDistToClosest(vector<int>& seats) {
    int res = 0;
    int last = -1, cur = 0;
    int n_seat = seats.size();
    // cout << "n_seat: " << n_seat << endl;
    while (!seats[cur]) ++cur;
    res = max(res, cur - last - 1);
    last = cur;
    while (cur < n_seat) {
      // cout << "-------------------- cur: " << cur << endl;
      if (seats[cur]) {
        res = max(res, ((cur - last) >> 1));
        last = cur;
      }
      ++cur;
    }
    res = max(res, n_seat - last - 1);
    // cout << "res: " << res << endl;
    return res;
  }
};

template<typename INPUT1_T, typename ANS_T>
void Case1()
{
  INPUT1_T input1;
  ANS_T right_ans, your_ans;
  input1 = {
    1,0,0,0,1,0,1
  };
  right_ans = 2;
  your_ans = Solution().maxDistToClosest(input1);
  // print2D(your_ans);
  if (!judge< ANS_T >(your_ans, right_ans)) {
    cout << __func__ << " err your_ans: " << your_ans << " right_ans: " << right_ans << endl;
    exit(1);
  }
  cout << __func__ << " AC, your ans:" << your_ans << endl;
}

template<typename INPUT1_T, typename ANS_T>
void Case2()
{
  INPUT1_T input1;
  ANS_T right_ans, your_ans;
  input1 = {
    1,0,0,0
  };
  right_ans = 3;
  your_ans = Solution().maxDistToClosest(input1);
  // print2D(your_ans);
  if (!judge< ANS_T >(your_ans, right_ans)) {
    cout << __func__ << " err your_ans: " << your_ans << " right_ans: " << right_ans << endl;
    exit(1);
  }
  cout << __func__ << " AC, your ans:" << your_ans << endl;
}

template<typename INPUT1_T, typename ANS_T>
void Case3()
{
  INPUT1_T input1;
  ANS_T right_ans, your_ans;
  input1 = {
    0,1
  };
  right_ans = 1;
  your_ans = Solution().maxDistToClosest(input1);
  // print2D(your_ans);
  if (!judge< ANS_T >(your_ans, right_ans)) {
    cout << __func__ << " err your_ans: " << your_ans << " right_ans: " << right_ans << endl;
    exit(1);
  }
  cout << __func__ << " AC, your ans:" << your_ans << endl;
}

// 由于比较懒,只想改一个地方
#define INPUT1_TYPE vector<int>
#define ANS_TYPE int

void case_test()
{
  Case1<INPUT1_TYPE, ANS_TYPE>();
  Case2<INPUT1_TYPE, ANS_TYPE>();
  Case3<INPUT1_TYPE, ANS_TYPE>();
}

int main()
{
  case_test();
}

总结

如果想要做 leetcode 题目提高思维能力, 更好的应对应聘题目, 可以通过

  1. 搞懂题目所能够提供的信息
  2. 用优美地语言说明白解决这个题目的思路。
  3. 用编程语言的数据结构来实现想出来想法。
  4. 了解实现的优缺点,如何能够减少自己实现的时间复杂度和空间复杂度。
    通过对 leetcode 849 的讲解, 通过实践上面的步骤, 演示如何实践这个思路.
    并且最后提供一个本地调试 linux 代码
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值