【问题标题】:Cracking The Coding Interview - Circus Tower Problem (17.8)破解编码面试 - 马戏团塔问题(17.8)
【发布时间】:2019-09-29 19:32:31
【问题描述】:

问题:

我正在完成第 6 版的《Cracking the Coding Interview》,但遇到了 Circus Tower 问题(编号 17.8)。我有一个我认为在 O(N logN) 时间内运行的解决方案,但本书的解决方案(不同)说 O(N logN) 解决方案非常复杂,但我的代码不是。我需要一些帮助来确定我的解决方案是否正确,以及它是否真的在 O(N logN) 时间内运行。我想了解我为什么错(或正确),所以任何细节都会有所帮助。

这里是问题文本:

一个马戏团正在设计一个由站在彼此肩上的人组成的塔式程序。出于实用和审美的原因,每个人都必须比他或她下面的人更矮更轻。给定马戏团中每个人的身高和体重,编写一个方法来计算这样一个塔中可能存在的最大人数。

我的解决方案:

def circus_tower(people):
    
    # People is a list of tuples of length 2, where item[0] is their height, and item[1] is their weight.
    
    if len(people) == 0:
        return 0
    
    people = sorted(people, key=lambda x: x[0])  # Sort people by heights. O(P*logP).
    start = 0
    end = 0
    longest_sequence = 0
    
    for i in range(1, len(people)):  # O(P).
        if people[i][1] > people[i-1][1]:  # Weight check.
            end = i
        else:
            longest_sequence = end - start + 1
            start = i
            end = i
    
    return max(longest_sequence, end - start + 1)

以下是一些示例输入以及我的代码返回的内容:

circus_tower([(65, 100), (70, 150), (56, 90), (75, 190), (60, 95), (68, 110)])
6

circus_tower([(65, 100), (70, 150), (56, 90), (75, 190), (65, 95), (68, 110)])
4

circus_tower([(56, 90), (65, 95), (72, 100), (68, 90), (70, 150), (75, 190)])
2

circus_tower([])
0

【问题讨论】:

  • sorted的调用已经nlogn
  • 是的,据我所知,它在代码中具有最高的时间复杂度。
  • 你的代码是 O(N logN) + O(N) = O(2N logN) 所以你的代码效率不如书 O(N logN)
  • 本书的解决方案实际上是 O(N^2),但他们提到 O(N logN) 的解决方案对于面试解决方案或本书而言过于复杂。在我认为适合采访的时间段内写这篇文章对我来说似乎很奇怪。而且它不是很复杂。

标签: python python-3.x lis


【解决方案1】:

您的解决方案是错误的。如果你跑

circus_tower([[1,1],[1,7],[1,9],[2,2],[2,6],[3,3],[3,5],[4,4]])

它返回2,而最长的子序列([1,1]<[2,2]<[3,3]<[4,4])的长度为4。

您的代码的问题是您只能找到连续的子序列。

【讨论】:

  • 感谢您指出这个问题 - 我认为我的问题中缺少一些东西。我在想出一个保持算法 O(N logN) 的修复程序时遇到了麻烦,而且谷歌搜索也没有帮助。我会继续调查的。
  • 排序后,问题相当于在一个整数序列中找到一个最长的递增子序列。这在wikipedia 上得到了广泛的描述
【解决方案2】:

我还“找到”了一个简单的解决方案,无法理解我错在哪里:

module CircusTower
  class Person
    include Comparable

    attr_reader :height, :weight

    def initialize(height, weight)
      @height = height
      @weight = weight
    end

    def <=>(other)
      res = height <=> other.height

      if res == 0
        weight <=> other.weight
      else
        res
      end
    end
  end

  # Time=O(n * log n) Mem=O(n)
  def solve_b(people)
    sorted = people.sort.reverse

    res = []

    sorted.each do |person|
      if res.size == 0
        res << person
      else
        if res.last.height > person.height && res.last.weight > person.weight
          res << person
        end
      end
    end

    res.size
  end

  RSpec.describe 'CircusTower' do
    include CircusTower

    subject { solve_b(people) }

    context 'book example' do
      let(:people) do
        [
          Person.new(65, 100),
          Person.new(70, 150),
          Person.new(56, 90),
          Person.new(75, 190),
          Person.new(60, 95),
          Person.new(68, 110),
        ]
      end

      it { is_expected.to eq 6 }
    end

    context 'tricky example' do
      let(:people) do
        [
          Person.new(1,1),
          Person.new(1,7),
          Person.new(1,9),
          Person.new(2,2),
          Person.new(2,6),
          Person.new(3,3),
          Person.new(3,5),
          Person.new(4,4),
        ]
      end

      it { is_expected.to eq 4 }
    end
  end
end

【讨论】:

  • 哦,我知道我找到了我的“简单”解决方案的反例(1, 1), (2, 2), (3, 3), (4,1)
【解决方案3】:

有一个正确的解决方案

module CircusTowerSo
  class Person
    include Comparable

    attr_reader :height, :weight

    def initialize(height, weight)
      @height = height
      @weight = weight
    end

    def <=>(other)
      res = height <=> other.height

      if res == 0
        weight <=> other.weight
      else
        res
      end
    end

    def smaller?(other)
      height < other.height && weight < other.weight
    end

    def to_s
      "(#{height}, #{weight})"
    end

    def inspect
      to_s
    end
  end

  # Time=O(n * n) Mem=O(n * n)
  def solve_b(people)
    sorted = people.sort

    find_lis_by_weight(sorted).size
  end

  def find_lis_by_weight(people)
    longest_by_index_cache = people.each_with_index.map { |person, i| [i, [person]] }.to_h
    longest = []

    people.each_with_index do |person, index|
      res = longest_for_index(longest_by_index_cache, index, person)

      if res.size > longest.size
        longest = res
      end

      longest_by_index_cache[index] = res
    end

    longest
  end

  def longest_for_index(longest_by_index_cache, index, person)
    longest_prev_seq = []

    index.times do |i|
      prev_seq = longest_by_index_cache[i]

      if prev_seq.last.smaller?(person) && prev_seq.size > longest_prev_seq.size
        longest_prev_seq = prev_seq
      end
    end

    longest_prev_seq + [person]
  end


  RSpec.describe 'CircusTower' do
    include CircusTower

    subject { solve_b(people) }

    context 'book example' do
      let(:people) do
        [
          Person.new(65, 100),
          Person.new(70, 150),
          Person.new(56, 90),
          Person.new(75, 190),
          Person.new(60, 95),
          Person.new(68, 110),
        ]
      end

      it { is_expected.to eq 6 }
    end

    context 'tricky example' do
      let(:people) do
        [
          Person.new(1, 1),
          Person.new(1, 7),
          Person.new(1, 9),
          Person.new(2, 2),
          Person.new(2, 6),
          Person.new(3, 3),
          Person.new(3, 5),
          Person.new(4, 4),
        ]
      end

      it { is_expected.to eq 4 }
    end

    context 'more tricky example' do
      let(:people) do
        [
          Person.new(1, 1),
          Person.new(2, 2),
          Person.new(3, 3),
          Person.new(4, 1),
        ]
      end

      it { is_expected.to eq 3 }
    end
  end
end

https://github.com/holyketzer/ctci_v6上查看更多 CTCI 解决方案

【讨论】:

    【解决方案4】:

    我把问题分为三个部分。

    1. 插入数据HeightWeight对象,然后按高度或宽度排序。我已经按高度排序了

    2. 然后将值插入到地图中以获得具有最小重量的唯一高度。

    3. 之后我找到了体重的最长递增子序列。

      import java.util.*;
      public class CircusTower {
      private class HeightWeight implements Comparable<HeightWeight>{
          int height;
          int weight;
          HeightWeight(int height, int weight) {
              this.height = height;
              this.weight = weight;
          }
          @Override
          public int compareTo(HeightWeight other) {
              if(this.height == other.height){
                  return this.weight - other.weight;
              }else{
                  return this.height - other.height;
              }
          }
      }
      public static void main(String[] args) {
          int[][] arr = {{1,1},{1,7},{1,9},{2,2},{2,6},{3,3},{3,5},{4,4}};
          CircusTower ct = new CircusTower();
         System.out.println(ct.getMaxHeightTower(arr));
      }
      
      public int getMaxHeightTower(int[][] arr){
          List<HeightWeight> list = new ArrayList<>();
          int i =0;
          for(i =0; i<arr.length; i++){
              list.add(new HeightWeight(arr[i][0], arr[i][1]));
          }
          Collections.sort(list);
          Map<Integer, Integer> map = new HashMap<>();
          for(i =0; i<list.size(); i++){
              HeightWeight current = list.get(i);
              if(!map.containsKey(current.height)){
                  map.put(current.height, current.weight);
              }
          }
          int[] nums = map.values().stream().mapToInt(Integer::intValue).toArray();
          return getLIS(nums);
      }
      
      public int getLIS(int[] nums){
      
          int _l = nums.length;
          int[] out = new int[_l];
          int mx = Integer.MIN_VALUE;
      
          /*
          we initialize the array with ones because
          a single character has a subsequence of length one
          */
          Arrays.fill(out, 1);
      
          for(int i = 0; i < _l; i++)
      
              for(int j = i + 1; j < _l; j++){
                  /*
                  every iteration, all we're doing is checking what is the longest
                  increasing subsequence so far, till this point
                  */
                  if(nums[j] > nums[i])
                      out[j] = Math.max(out[j], out[i] + 1);
      
                  /*
                  we keep checking for a max value because the longest
                  subsequence could exist before the end of the string
                  */
                  mx = Math.max(out[j], mx);
              }
      
          return mx == Integer.MIN_VALUE ? 1 : mx;
      }
      

      }

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-09-20
      • 1970-01-01
      • 2018-11-21
      • 2013-09-05
      • 2015-12-06
      • 2015-12-12
      • 1970-01-01
      相关资源
      最近更新 更多