กำไรจากการขายครั้งเดียวสูงสุด


123

สมมติว่าเราได้รับอาร์เรย์ของจำนวนเต็มnแทนราคาหุ้นในวันเดียว เราต้องการหาคู่(buyDay, sellDay)โดยbuyDay ≤ sellDayเช่นว่าถ้าเราซื้อหุ้นในbuyDayและขายในsellDayเราจะเพิ่มกำไรให้สูงสุด

เห็นได้ชัดว่ามีโซลูชันO (n 2 )สำหรับอัลกอริทึมโดยการลองใช้คู่(buyDay, sellDay) ที่เป็นไปได้ทั้งหมดและใช้สิ่งที่ดีที่สุดจากทั้งหมด อย่างไรก็ตามมีอัลกอริทึมที่ดีกว่าหรืออาจเป็นอัลกอริทึมที่ทำงานในเวลาO (n) ?


2
นี่คือปัญหาผลรวมสูงสุดในลำดับต่อมาที่มีระดับความไม่แน่นอน
MSN

2
@MSN: ยังไง? เขาไม่ได้ดูที่ผลรวมเลย แต่เป็นที่ความแตกต่างระหว่างองค์ประกอบ
PengOne

@ PengOne- จริง แต่คำถามนั้นถูกปิด ฉันตั้งคำถามใหม่เพื่อให้เข้าใจง่ายขึ้นดังนั้นเราจะพยายามเปิดคำถามนี้ไว้ได้ไหม
templatetypedef

2
@PengOne อย่างที่บอกมันมีทิศทางระดับหนึ่ง โดยเฉพาะอย่างยิ่งคุณต้องการเพิ่มผลรวมของกำไร / ขาดทุนในช่วงวันที่ต่อเนื่องกัน ดังนั้นให้แปลงรายการเป็นกำไร / ขาดทุนและหาผลรวมสูงสุดในภายหลัง
MSN

1
@PDN: จะไม่ทำงานเพราะนาทีอาจเกิดขึ้นก่อนสูงสุด คุณไม่สามารถขายหุ้นได้ (ในกรณีนี้) และซื้อในภายหลัง
Ajeet Ganga

คำตอบ:


287

ฉันรักปัญหานี้ เป็นคำถามสัมภาษณ์คลาสสิกและขึ้นอยู่กับว่าคุณคิดอย่างไรคุณจะได้รับคำตอบที่ดีขึ้นและดีขึ้น เป็นไปได้อย่างแน่นอนที่จะทำสิ่งนี้ได้ดีกว่า O (n 2 ) และฉันได้ระบุวิธีต่างๆไว้สามวิธีที่คุณสามารถคิดเกี่ยวกับปัญหาได้ที่นี่ หวังว่าจะตอบคำถามของคุณได้!

ขั้นแรกวิธีแก้ปัญหาแบ่งและพิชิต ลองดูว่าเราสามารถแก้ปัญหานี้ได้หรือไม่โดยการแบ่งอินพุตออกเป็นครึ่งหนึ่งแก้ปัญหาในแต่ละ subarray จากนั้นรวมทั้งสองเข้าด้วยกัน ปรากฎว่าเราทำได้จริงและทำได้อย่างมีประสิทธิภาพ! สัญชาตญาณมีดังนี้ หากเรามีเวลาเพียงวันเดียวทางเลือกที่ดีที่สุดคือซื้อในวันนั้นแล้วขายคืนในวันเดียวกันโดยไม่มีกำไร มิฉะนั้นให้แบ่งอาร์เรย์ออกเป็นสองส่วน หากเราคิดว่าคำตอบที่ดีที่สุดคืออะไรคำตอบนั้นจะต้องเป็นหนึ่งในสามที่

  1. คู่ซื้อ / ขายที่ถูกต้องจะเกิดขึ้นอย่างสมบูรณ์ภายในครึ่งแรก
  2. คู่ซื้อ / ขายที่ถูกต้องจะเกิดขึ้นอย่างสมบูรณ์ภายในครึ่งหลัง
  3. คู่ซื้อ / ขายที่ถูกต้องเกิดขึ้นในทั้งสองครึ่ง - เราซื้อในครึ่งแรกจากนั้นขายในครึ่งหลัง

เราสามารถรับค่าสำหรับ (1) และ (2) ได้โดยการเรียกใช้อัลกอริทึมซ้ำ ๆ ในครึ่งแรกและครึ่งที่สอง สำหรับตัวเลือก (3) วิธีที่จะทำกำไรสูงสุดคือซื้อที่จุดต่ำสุดในครึ่งปีแรกและขายในจุดที่ยิ่งใหญ่ที่สุดในครึ่งปีหลัง เราสามารถค้นหาค่าต่ำสุดและค่าสูงสุดในสองส่วนครึ่งได้โดยทำการสแกนเชิงเส้นอย่างง่ายบนอินพุตและค้นหาทั้งสองค่า จากนั้นจะให้อัลกอริทึมที่มีการเกิดซ้ำดังต่อไปนี้:

T(1) <= O(1)
T(n) <= 2T(n / 2) + O(n)

การใช้Master Theoremเพื่อแก้ไขการเกิดซ้ำเราพบว่าสิ่งนี้ทำงานในเวลา O (n lg n) และจะใช้พื้นที่ O (lg n) สำหรับการเรียกซ้ำ เราเพิ่งเอาชนะวิธีแก้ปัญหาO (n 2 ) ที่ไร้เดียงสา!

แต่เดี๋ยวก่อน! เราทำได้ดีกว่านี้อีกมาก สังเกตว่าเหตุผลเดียวที่เรามีคำ O (n) ในการเกิดซ้ำของเราคือเราต้องสแกนอินพุตทั้งหมดเพื่อพยายามหาค่าต่ำสุดและสูงสุดในแต่ละครึ่ง เนื่องจากเราทำการสำรวจซ้ำในแต่ละครึ่งอยู่แล้วบางทีเราอาจจะทำได้ดีกว่านี้โดยให้การเรียกซ้ำยังส่งคืนค่าต่ำสุดและสูงสุดที่เก็บไว้ในแต่ละครึ่ง! กล่าวอีกนัยหนึ่งการเรียกซ้ำของเราให้กลับสามสิ่ง:

  1. เวลาซื้อและขายเพื่อเพิ่มผลกำไร
  2. ค่าต่ำสุดโดยรวมในช่วง
  3. ค่าสูงสุดโดยรวมในช่วง

ค่าสองค่าสุดท้ายนี้สามารถคำนวณแบบวนซ้ำได้โดยใช้การเรียกซ้ำแบบตรงไปตรงมาซึ่งเราสามารถเรียกใช้พร้อมกันกับการเรียกซ้ำเพื่อคำนวณ (1):

  1. ค่าสูงสุดและต่ำสุดของช่วงองค์ประกอบเดียวเป็นเพียงองค์ประกอบนั้น
  2. ค่าสูงสุดและต่ำสุดของช่วงองค์ประกอบหลายช่วงสามารถพบได้โดยการแบ่งอินพุตเป็นครึ่งหนึ่งค้นหาค่าสูงสุดและต่ำสุดของแต่ละครึ่งจากนั้นหาค่าสูงสุดและต่ำสุดตามลำดับ

หากเราใช้แนวทางนี้ความสัมพันธ์การเกิดซ้ำของเราจะอยู่ในขณะนี้

T(1) <= O(1)
T(n) <= 2T(n / 2) + O(1)

การใช้ Master Theorem ที่นี่ทำให้เรามีรันไทม์ O (n) พร้อม O (lg n) ซึ่งดีกว่าโซลูชันเดิมของเรา!

แต่เดี๋ยวก่อนเราจะทำได้ดีกว่านี้! ลองคิดเกี่ยวกับการแก้ปัญหานี้โดยใช้โปรแกรมไดนามิก แนวคิดที่จะคิดเกี่ยวกับปัญหาดังนี้ สมมติว่าเรารู้คำตอบของปัญหาหลังจากดูองค์ประกอบ k แรกแล้ว เราสามารถใช้ความรู้เกี่ยวกับองค์ประกอบ (k + 1) st รวมกับวิธีแก้ปัญหาเบื้องต้นเพื่อแก้ปัญหาสำหรับองค์ประกอบแรก (k + 1) ได้หรือไม่ ถ้าเป็นเช่นนั้นเราจะได้อัลกอริทึมที่ยอดเยี่ยมโดยการแก้ปัญหาสำหรับองค์ประกอบแรกจากนั้นสองตัวแรกจากนั้นสามตัวแรก ฯลฯ จนกว่าเราจะคำนวณมันสำหรับ n องค์ประกอบแรก

ลองคิดดูว่าจะทำอย่างไร หากเรามีเพียงองค์ประกอบเดียวเราก็รู้แล้วว่าต้องเป็นคู่ซื้อ / ขายที่ดีที่สุด ตอนนี้สมมติว่าเรารู้คำตอบที่ดีที่สุดสำหรับองค์ประกอบ k แรกและดูที่องค์ประกอบ (k + 1) st วิธีเดียวที่ค่านี้จะสร้างโซลูชันได้ดีกว่าที่เรามีสำหรับองค์ประกอบ k แรกคือถ้าความแตกต่างระหว่างองค์ประกอบ k ที่เล็กที่สุดกับองค์ประกอบใหม่นั้นใหญ่กว่าความแตกต่างที่ใหญ่ที่สุดที่เราคำนวณไว้ ดังนั้นสมมติว่าเมื่อเราสำรวจองค์ประกอบต่างๆเราจะติดตามค่าสองค่านั่นคือมูลค่าต่ำสุดที่เราเห็นจนถึงตอนนี้และผลกำไรสูงสุดที่เราทำได้ด้วยองค์ประกอบ k แรกเท่านั้น ในตอนแรกมูลค่าต่ำสุดที่เราเห็นจนถึงตอนนี้คือองค์ประกอบแรกและกำไรสูงสุดคือศูนย์ เมื่อเราเห็นองค์ประกอบใหม่ อันดับแรกเราจะอัปเดตผลกำไรที่ดีที่สุดของเราโดยคำนวณว่าเราจะทำได้เท่าไรโดยซื้อในราคาต่ำสุดที่เห็นจนถึงตอนนี้และขายในราคาปัจจุบัน หากสิ่งนี้ดีกว่ามูลค่าที่เหมาะสมที่สุดที่เราคำนวณไว้เราจะอัปเดตโซลูชันที่ดีที่สุดเพื่อเป็นผลกำไรใหม่นี้ ต่อไปเราจะอัปเดตองค์ประกอบขั้นต่ำที่เห็นจนถึงตอนนี้เป็นค่าต่ำสุดขององค์ประกอบที่เล็กที่สุดในปัจจุบันและองค์ประกอบใหม่

เนื่องจากในแต่ละขั้นตอนเราทำงาน O (1) เพียงอย่างเดียวและเราจะไปที่องค์ประกอบ n แต่ละองค์ประกอบเพียงครั้งเดียวซึ่งต้องใช้เวลา O (n) ในการดำเนินการให้เสร็จสิ้น! ยิ่งไปกว่านั้นยังใช้ที่เก็บข้อมูลเสริม O (1) เท่านั้น นี่เป็นสิ่งที่ดีที่สุดเท่าที่เราเคยมีมา!

ตัวอย่างเช่นในอินพุตของคุณนี่คือวิธีการทำงานของอัลกอริทึมนี้ ตัวเลขที่อยู่ระหว่างแต่ละค่าของอาร์เรย์จะสอดคล้องกับค่าที่อัลกอริทึมถือไว้ ณ จุดนั้น คุณจะไม่จัดเก็บสิ่งเหล่านี้ทั้งหมดจริงๆ (จะต้องใช้หน่วยความจำ O (n)!) แต่การดูอัลกอริทึมวิวัฒนาการจะเป็นประโยชน์:

            5        10        4          6         7
min         5         5        4          4         4    
best      (5,5)     (5,10)   (5,10)     (5,10)    (5,10)

คำตอบ: (5, 10)

            5        10        4          6        12
min         5         5        4          4         4    
best      (5,5)     (5,10)   (5,10)     (5,10)    (4,12)

คำตอบ: (4, 12)

            1       2       3      4      5
min         1       1       1      1      1
best      (1,1)   (1,2)   (1,3)  (1,4)  (1,5)

คำตอบ: (1, 5)

เราจะทำได้ดีกว่านี้ไหม น่าเสียดายที่ไม่ใช่ในแง่ที่ไม่แสดงอาการ หากเราใช้เวลาน้อยกว่า O (n) เราจะไม่สามารถดูตัวเลขทั้งหมดในอินพุตขนาดใหญ่ได้ดังนั้นจึงไม่สามารถรับประกันได้ว่าเราจะไม่พลาดคำตอบที่ดีที่สุด (เราสามารถ "ซ่อน" ไว้ในองค์ประกอบที่เรา ไม่ได้ดู) นอกจากนี้เราไม่สามารถใช้พื้นที่น้อยกว่า O (1) ได้ อาจมีการปรับให้เหมาะสมกับปัจจัยคงที่ที่ซ่อนอยู่ในสัญกรณ์ big-O แต่อย่างอื่นเราไม่สามารถคาดหวังว่าจะพบตัวเลือกที่ดีกว่าอย่างสิ้นเชิง

โดยรวมแล้วหมายความว่าเรามีอัลกอริทึมต่อไปนี้:

  • ไร้เดียงสา: O (n 2 ) เวลาพื้นที่ O (1)
  • Divide-and-Conquer: เวลา O (n lg n) พื้นที่ O (lg n)
  • เพิ่มประสิทธิภาพ Divide-and-Conquer: O (n) time, O (lg n) space
  • การเขียนโปรแกรมแบบไดนามิก: เวลา O (n) พื้นที่ O (1)

หวังว่านี่จะช่วยได้!

แก้ไข : หากคุณสนใจฉันได้เขียนโค้ดอัลกอริทึมทั้งสี่นี้ในเวอร์ชัน Pythonเพื่อให้คุณสามารถเล่นกับพวกเขาและตัดสินการแสดงที่สัมพันธ์กันได้ นี่คือรหัส:

# Four different algorithms for solving the maximum single-sell profit problem,
# each of which have different time and space complexity.  This is one of my
# all-time favorite algorithms questions, since there are so many different
# answers that you can arrive at by thinking about the problem in slightly
# different ways.
#
# The maximum single-sell profit problem is defined as follows.  You are given
# an array of stock prices representing the value of some stock over time.
# Assuming that you are allowed to buy the stock exactly once and sell the
# stock exactly once, what is the maximum profit you can make?  For example,
# given the prices
#
#                        2, 7, 1, 8, 2, 8, 4, 5, 9, 0, 4, 5
#
# The maximum profit you can make is 8, by buying when the stock price is 1 and
# selling when the stock price is 9.  Note that while the greatest difference
# in the array is 9 (by subtracting 9 - 0), we cannot actually make a profit of
# 9 here because the stock price of 0 comes after the stock price of 9 (though
# if we wanted to lose a lot of money, buying high and selling low would be a
# great idea!)
#
# In the event that there's no profit to be made at all, we can always buy and
# sell on the same date.  For example, given these prices (which might
# represent a buggy-whip manufacturer:)
#
#                            9, 8, 7, 6, 5, 4, 3, 2, 1, 0
#
# The best profit we can make is 0 by buying and selling on the same day.
#
# Let's begin by writing the simplest and easiest algorithm we know of that
# can solve this problem - brute force.  We will just consider all O(n^2) pairs
# of values, and then pick the one with the highest net profit.  There are
# exactly n + (n - 1) + (n - 2) + ... + 1 = n(n + 1)/2 different pairs to pick
# from, so this algorithm will grow quadratically in the worst-case.  However,
# it uses only O(1) memory, which is a somewhat attractive feature.  Plus, if
# our first intuition for the problem gives a quadratic solution, we can be
# satisfied that if we don't come up with anything else, we can always have a
# polynomial-time solution.

def BruteForceSingleSellProfit(arr):
    # Store the best possible profit we can make; initially this is 0.
    bestProfit = 0;

    # Iterate across all pairs and find the best out of all of them.  As a
    # minor optimization, we don't consider any pair consisting of a single
    # element twice, since we already know that we get profit 0 from this.
    for i in range(0, len(arr)):
        for j in range (i + 1, len(arr)):
            bestProfit = max(bestProfit, arr[j] - arr[i])

    return bestProfit

# This solution is extremely inelegant, and it seems like there just *has* to
# be a better solution.  In fact, there are many better solutions, and we'll
# see three of them.
#
# The first insight comes if we try to solve this problem by using a divide-
# and-conquer strategy.  Let's consider what happens if we split the array into
# two (roughly equal) halves.  If we do so, then there are three possible
# options about where the best buy and sell times are:
#
# 1. We should buy and sell purely in the left half of the array.
# 2. We should buy and sell purely in the right half of the array.
# 3. We should buy in the left half of the array and sell in the right half of
#    the array.
#
# (Note that we don't need to consider selling in the left half of the array
# and buying in the right half of the array, since the buy time must always
# come before the sell time)
#
# If we want to solve this problem recursively, then we can get values for (1)
# and (2) by recursively invoking the algorithm on the left and right
# subarrays.  But what about (3)?  Well, if we want to maximize our profit, we
# should be buying at the lowest possible cost in the left half of the array
# and selling at the highest possible cost in the right half of the array.
# This gives a very elegant algorithm for solving this problem:
#
#    If the array has size 0 or size 1, the maximum profit is 0.
#    Otherwise:
#       Split the array in half.
#       Compute the maximum single-sell profit in the left array, call it L.
#       Compute the maximum single-sell profit in the right array, call it R.
#       Find the minimum of the first half of the array, call it Min
#       Find the maximum of the second half of the array, call it Max
#       Return the maximum of L, R, and Max - Min.
#
# Let's consider the time and space complexity of this algorithm.  Our base
# case takes O(1) time, and in our recursive step we make two recursive calls,
# one on each half of the array, and then does O(n) work to scan the array
# elements to find the minimum and maximum values.  This gives the recurrence
#
#    T(1)     = O(1)
#    T(n / 2) = 2T(n / 2) + O(n)
#
# Using the Master Theorem, this recurrence solves to O(n log n), which is
# asymptotically faster than our original approach!  However, we do pay a
# (slight) cost in memory usage.  Because we need to maintain space for all of
# the stack frames we use.  Since on each recursive call we cut the array size
# in half, the maximum number of recursive calls we can make is O(log n), so
# this algorithm uses O(n log n) time and O(log n) memory.

def DivideAndConquerSingleSellProfit(arr):
    # Base case: If the array has zero or one elements in it, the maximum
    # profit is 0.
    if len(arr) <= 1:
        return 0;

    # Cut the array into two roughly equal pieces.
    left  = arr[ : len(arr) / 2]
    right = arr[len(arr) / 2 : ]

    # Find the values for buying and selling purely in the left or purely in
    # the right.
    leftBest  = DivideAndConquerSingleSellProfit(left)
    rightBest = DivideAndConquerSingleSellProfit(right)

    # Compute the best profit for buying in the left and selling in the right.
    crossBest = max(right) - min(left)

    # Return the best of the three
    return max(leftBest, rightBest, crossBest)

# While the above algorithm for computing the maximum single-sell profit is
# better timewise than what we started with (O(n log n) versus O(n^2)), we can
# still improve the time performance.  In particular, recall our recurrence
# relation:
#
#    T(1) = O(1)
#    T(n) = 2T(n / 2) + O(n)
#
# Here, the O(n) term in the T(n) case comes from the work being done to find
# the maximum and minimum values in the right and left halves of the array,
# respectively.  If we could find these values faster than what we're doing
# right now, we could potentially decrease the function's runtime.
#
# The key observation here is that we can compute the minimum and maximum
# values of an array using a divide-and-conquer approach.  Specifically:
#
#    If the array has just one element, it is the minimum and maximum value.
#    Otherwise:
#       Split the array in half.
#       Find the minimum and maximum values from the left and right halves.
#       Return the minimum and maximum of these two values.
#
# Notice that our base case does only O(1) work, and our recursive case manages
# to do only O(1) work in addition to the recursive calls.  This gives us the
# recurrence relation
#
#    T(1) = O(1)
#    T(n) = 2T(n / 2) + O(1)
#
# Using the Master Theorem, this solves to O(n).
#
# How can we make use of this result?  Well, in our current divide-and-conquer
# solution, we split the array in half anyway to find the maximum profit we
# could make in the left and right subarrays.  Could we have those recursive
# calls also hand back the maximum and minimum values of the respective arrays?
# If so, we could rewrite our solution as follows:
#
#    If the array has size 1, the maximum profit is zero and the maximum and
#       minimum values are the single array element.
#    Otherwise:
#       Split the array in half.
#       Compute the maximum single-sell profit in the left array, call it L.
#       Compute the maximum single-sell profit in the right array, call it R.
#       Let Min be the minimum value in the left array, which we got from our
#           first recursive call.
#       Let Max be the maximum value in the right array, which we got from our
#           second recursive call.
#       Return the maximum of L, R, and Max - Min for the maximum single-sell
#           profit, and the appropriate maximum and minimum values found from
#           the recursive calls.
#
# The correctness proof for this algorithm works just as it did before, but now
# we never actually do a scan of the array at each step.  In fact, we do only
# O(1) work at each level.  This gives a new recurrence
#
#     T(1) = O(1)
#     T(n) = 2T(n / 2) + O(1)
#
# Which solves to O(n).  We're now using O(n) time and O(log n) memory, which
# is asymptotically faster than before!
#
# The code for this is given below:

def OptimizedDivideAndConquerSingleSellProfit(arr):
    # If the array is empty, the maximum profit is zero.
    if len(arr) == 0:
        return 0

    # This recursive helper function implements the above recurrence.  It
    # returns a triple of (max profit, min array value, max array value).  For
    # efficiency reasons, we always reuse the array and specify the bounds as
    # [lhs, rhs]
    def Recursion(arr, lhs, rhs):
        # If the array has just one element, we return that the profit is zero
        # but the minimum and maximum values are just that array value.
        if lhs == rhs:
            return (0, arr[lhs], arr[rhs])

        # Recursively compute the values for the first and latter half of the
        # array.  To do this, we need to split the array in half.  The line
        # below accomplishes this in a way that, if ported to other languages,
        # cannot result in an integer overflow.
        mid = lhs + (rhs - lhs) / 2

        # Perform the recursion.
        ( leftProfit,  leftMin,  leftMax) = Recursion(arr, lhs, mid)
        (rightProfit, rightMin, rightMax) = Recursion(arr, mid + 1, rhs)

        # Our result is the maximum possible profit, the minimum of the two
        # minima we've found (since the minimum of these two values gives the
        # minimum of the overall array), and the maximum of the two maxima.
        maxProfit = max(leftProfit, rightProfit, rightMax - leftMin)
        return (maxProfit, min(leftMin, rightMin), max(leftMax, rightMax))

    # Using our recursive helper function, compute the resulting value.
    profit, _, _ = Recursion(arr, 0, len(arr) - 1)
    return profit

# At this point we've traded our O(n^2)-time, O(1)-space solution for an O(n)-
# time, O(log n) space solution.  But can we do better than this?
#
# To find a better algorithm, we'll need to switch our line of reasoning.
# Rather than using divide-and-conquer, let's see what happens if we use
# dynamic programming.  In particular, let's think about the following problem.
# If we knew the maximum single-sell profit that we could get in just the first
# k array elements, could we use this information to determine what the
# maximum single-sell profit would be in the first k + 1 array elements?  If we
# could do this, we could use the following algorithm:
#
#   Find the maximum single-sell profit to be made in the first 1 elements.
#   For i = 2 to n:
#      Compute the maximum single-sell profit using the first i elements.
#
# How might we do this?  One intuition is as follows.  Suppose that we know the
# maximum single-sell profit of the first k elements.  If we look at k + 1
# elements, then either the maximum profit we could make by buying and selling
# within the first k elements (in which case nothing changes), or we're
# supposed to sell at the (k + 1)st price.  If we wanted to sell at this price
# for a maximum profit, then we would want to do so by buying at the lowest of
# the first k + 1 prices, then selling at the (k + 1)st price.
#
# To accomplish this, suppose that we keep track of the minimum value in the
# first k elements, along with the maximum profit we could make in the first
# k elements.  Upon seeing the (k + 1)st element, we update what the current
# minimum value is, then update what the maximum profit we can make is by
# seeing whether the difference between the (k + 1)st element and the new
# minimum value is.  Note that it doesn't matter what order we do this in; if
# the (k + 1)st element is the smallest element so far, there's no possible way
# that we could increase our profit by selling at that point.
#
# To finish up this algorithm, we should note that given just the first price,
# the maximum possible profit is 0.
#
# This gives the following simple and elegant algorithm for the maximum single-
# sell profit problem:
#
#   Let profit = 0.
#   Let min = arr[0]
#   For k = 1 to length(arr):
#       If arr[k] < min, set min = arr[k]
#       If profit < arr[k] - min, set profit = arr[k] - min
#
# This is short, sweet, and uses only O(n) time and O(1) memory.  The beauty of
# this solution is that we are quite naturally led there by thinking about how
# to update our answer to the problem in response to seeing some new element.
# In fact, we could consider implementing this algorithm as a streaming
# algorithm, where at each point in time we maintain the maximum possible
# profit and then update our answer every time new data becomes available.
#
# The final version of this algorithm is shown here:

def DynamicProgrammingSingleSellProfit(arr):
    # If the array is empty, we cannot make a profit.
    if len(arr) == 0:
        return 0

    # Otherwise, keep track of the best possible profit and the lowest value
    # seen so far.
    profit = 0
    cheapest = arr[0]

    # Iterate across the array, updating our answer as we go according to the
    # above pseudocode.
    for i in range(1, len(arr)):
        # Update the minimum value to be the lower of the existing minimum and
        # the new minimum.
        cheapest = min(cheapest, arr[i])

        # Update the maximum profit to be the larger of the old profit and the
        # profit made by buying at the lowest value and selling at the current
        # price.
        profit = max(profit, arr[i] - cheapest)

    return profit

# To summarize our algorithms, we have seen
#
# Naive:                        O(n ^ 2)   time, O(1)     space
# Divide-and-conquer:           O(n log n) time, O(log n) space
# Optimized divide-and-conquer: O(n)       time, O(log n) space
# Dynamic programming:          O(n)       time, O(1)     space

1
@ FrankQ - จำเป็นต้องมีช่องว่างสำหรับการโทรซ้ำทั้งสองครั้ง แต่โดยทั่วไปแล้วการโทรเหล่านั้นจะดำเนินการทีละรายการ ซึ่งหมายความว่าคอมไพเลอร์สามารถใช้หน่วยความจำซ้ำระหว่างการโทร เมื่อการโทรหนึ่งครั้งกลับมาสายถัดไปสามารถใช้พื้นที่ของมันซ้ำได้ ด้วยเหตุนี้คุณจึงต้องใช้หน่วยความจำในการเรียกใช้ฟังก์ชันหนึ่งครั้งเท่านั้นดังนั้นการใช้หน่วยความจำจึงเป็นสัดส่วนกับความลึกสูงสุดของ call stack เนื่องจากการเรียกซ้ำสิ้นสุดที่ระดับ O (log n) จึงต้องใช้หน่วยความจำ O (log n) เท่านั้น สิ่งนั้นให้ความกระจ่างหรือไม่?
templatetypedef

ใครสามารถโอนสิ่งเหล่านี้ไปยัง Ruby ได้บ้าง? การเรียกซ้ำบางอย่างไม่ได้ผลเช่นเดียวกับใน Python นอกจากนี้โซลูชันเหล่านี้จะคืนกำไรสูงสุดเท่านั้น พวกเขาไม่ส่งคืนคะแนนอาร์เรย์ที่ให้ผลกำไร (ซึ่งสามารถใช้เพื่อรายงานเปอร์เซ็นต์ของการเพิ่มขึ้นของกำไรในอดีต)
rcd

แนวคิดของการเขียนโปรแกรมแบบไดนามิกไม่จำเป็นต้องอธิบายวิธีแก้ปัญหาเวลา O (n) แต่เป็นการดีที่คุณจะเชื่อมโยงอัลกอริทึมประเภทนี้ทั้งหมดเข้าด้วยกัน
222

คุณจะสร้างอัลกอริทึม O (n ^ 2) ย่อยใด ๆ เพื่อค้นหาคู่ทั้งหมดที่เรียงตามผลกำไรได้อย่างไร
ferk86

@templatetypedef เราจะเปลี่ยนแนวทางการเขียนโปรแกรมแบบไดนามิกได้อย่างไรหากเราเริ่มต้นด้วยงบประมาณ M $ และแทนที่จะเป็นหุ้นเดี่ยวเรามีหุ้นที่มีราคามากกว่า n วันตามที่กำหนด เช่นเรามีจำนวนหน่วยหุ้นที่ซื้อแตกต่างกันและข้อมูลหุ้นที่มีจาก 1 หุ้นเป็น n หุ้น (เช่นเดียวกับที่เรามีเฉพาะสำหรับ Google ตอนนี้เรามีสำหรับ บริษัท อื่น ๆ อีก 5 แห่งเช่นกัน)
Ronak Agrawal

32

นี่คือปัญหาผลรวมสูงสุดในลำดับต่อมาที่มีค่าเล็กน้อย ปัญหาผลรวมสูงสุดตามมาคือรายการของจำนวนเต็มซึ่งอาจเป็นบวกหรือลบค้นหาผลรวมที่ใหญ่ที่สุดของเซตย่อยที่ต่อเนื่องกันของรายการนั้น

คุณสามารถแปลงปัญหานี้เป็นปัญหานั้นได้เล็กน้อยโดยรับผลกำไรหรือขาดทุนระหว่างวันติดต่อกัน ดังนั้นคุณจะเปลี่ยนรายการของราคาหุ้นเช่น[5, 6, 7, 4, 2]ในรายการของกำไร / [1, 1, -3, -2]ขาดทุนเช่น, ปัญหาผลรวมในลำดับต่อมานั้นค่อนข้างง่ายในการแก้ปัญหา: ค้นหาผลลัพธ์ที่ตามมาด้วยผลรวมขององค์ประกอบที่ใหญ่ที่สุดในอาร์เรย์


1
ผมไม่คิดว่ามันค่อนข้างทำงานออกวิธีนี้เพราะถ้าคุณซื้อหุ้นในบางวันเริ่มต้นที่คุณไม่ได้รับประโยชน์ของเดลต้าจากวันก่อนหน้า หรือนี่ไม่ใช่ปัญหาในแนวทางนี้?
templatetypedef

1
@templatetypedef นั่นคือเหตุผลที่คุณติดตามผลรวมที่มากที่สุดและผลรวมลำดับปัจจุบัน เมื่อผลรวมลำดับปัจจุบันต่ำกว่าศูนย์คุณจะรู้ว่าคุณจะไม่ได้ทำเงินใด ๆ กับลำดับนั้นและคุณสามารถเริ่มต้นใหม่ได้อีกครั้ง ด้วยการติดตามผลรวมที่มากที่สุดคุณจะพบวันที่ซื้อ / ขายที่ดีที่สุดโดยอัตโนมัติ
MSN

6
@templatetypedef บังเอิญคุณทำสิ่งเดียวกันกับคำตอบของคุณ
MSN

16

ฉันไม่แน่ใจจริงๆว่าทำไมจึงถือว่าเป็นคำถามเกี่ยวกับการเขียนโปรแกรมแบบไดนามิก ฉันเคยเห็นคำถามนี้ในหนังสือเรียนและคู่มืออัลกอริทึมโดยใช้รันไทม์ O (n log n) และ O (log n) สำหรับช่องว่าง (เช่นองค์ประกอบของการสัมภาษณ์การเขียนโปรแกรม) ดูเหมือนว่าจะเป็นปัญหาที่ง่ายกว่าที่คนทั่วไปจะทำเสียอีก

สิ่งนี้ทำงานโดยการติดตามผลกำไรสูงสุดราคาซื้อขั้นต่ำและราคาซื้อ / ขายที่เหมาะสมที่สุด เมื่อผ่านแต่ละองค์ประกอบในอาร์เรย์จะตรวจสอบว่าองค์ประกอบที่กำหนดมีขนาดเล็กกว่าราคาซื้อขั้นต่ำหรือไม่ หากเป็นเช่นนั้นดัชนีราคาซื้อขั้นต่ำ ( min) จะได้รับการอัปเดตเป็นดัชนีขององค์ประกอบนั้น นอกจากนี้สำหรับแต่ละองค์ประกอบbecomeABillionaireอัลกอริทึมจะตรวจสอบว่าarr[i] - arr[min](ความแตกต่างระหว่างองค์ประกอบปัจจุบันและราคาซื้อขั้นต่ำ) มากกว่ากำไรปัจจุบันหรือไม่ หากเป็นเช่นนั้นกำไรจะอัปเดตเป็นส่วนต่างนั้นและตั้งค่าการซื้อเป็นarr[min]arr[i]และการขายมีการตั้งค่า

ทำงานในรอบเดียว

static void becomeABillionaire(int arr[]) {
    int i = 0, buy = 0, sell = 0, min = 0, profit = 0;

    for (i = 0; i < arr.length; i++) {
        if (arr[i] < arr[min])
            min = i;
        else if (arr[i] - arr[min] > profit) {
            buy = min; 
            sell = i;
            profit = arr[i] - arr[min];
        }

    }

    System.out.println("We will buy at : " + arr[buy] + " sell at " + arr[sell] + 
            " and become billionaires worth " + profit );

}

ผู้เขียนร่วม: https://stackoverflow.com/users/599402/ephraim


2

ปัญหานี้เหมือนกับลำดับย่อยสูงสุดที่
ฉันแก้ไขโดยใช้การเขียนโปรแกรมแบบไดนามิก ติดตามปัจจุบันและก่อนหน้า (กำไรวันที่ซื้อและวันที่ขาย) หากปัจจุบันสูงกว่าก่อนหน้าให้แทนที่ก่อนหน้านี้ด้วยปัจจุบัน

    int prices[] = { 38, 37, 35, 31, 20, 24, 35, 21, 24, 21, 23, 20, 23, 25, 27 };

    int buyDate = 0, tempbuyDate = 0;
    int sellDate = 0, tempsellDate = 0; 

    int profit = 0, tempProfit =0;
    int i ,x = prices.length;
    int previousDayPrice = prices[0], currentDayprice=0;

    for(i=1 ; i<x; i++ ) {

        currentDayprice = prices[i];

        if(currentDayprice > previousDayPrice ) {  // price went up

            tempProfit = tempProfit + currentDayprice - previousDayPrice;
            tempsellDate = i;
        }
        else { // price went down 

            if(tempProfit>profit) { // check if the current Profit is higher than previous profit....

                profit = tempProfit;
                sellDate = tempsellDate;
                buyDate = tempbuyDate;
            } 
                                     // re-intialized buy&sell date, profit....
                tempsellDate = i;
                tempbuyDate = i;
                tempProfit =0;
        }
        previousDayPrice = currentDayprice;
    }

    // if the profit is highest till the last date....
    if(tempProfit>profit) {
        System.out.println("buydate " + tempbuyDate + " selldate " + tempsellDate + " profit " + tempProfit );
    }
    else {
        System.out.println("buydate " + buyDate + " selldate " + sellDate + " profit " + profit );
    }   

2

นี่คือโซลูชัน Java ของฉัน:

public static void main(String[] args) {
    int A[] = {5,10,4,6,12};

    int min = A[0]; // Lets assume first element is minimum
    int maxProfit = 0; // 0 profit, if we buy & sell on same day.
    int profit = 0;
    int minIndex = 0; // Index of buy date
    int maxIndex = 0; // Index of sell date

    //Run the loop from next element
    for (int i = 1; i < A.length; i++) {
        //Keep track of minimum buy price & index
        if (A[i] < min) {
            min = A[i];
            minIndex = i;
        }
        profit = A[i] - min;
        //If new profit is more than previous profit, keep it and update the max index
        if (profit > maxProfit) {
            maxProfit = profit;
            maxIndex = i;
        }
    }
    System.out.println("maxProfit is "+maxProfit);
    System.out.println("minIndex is "+minIndex);
    System.out.println("maxIndex is "+maxIndex);     
}

@Nitiraj ใช่วิธีแก้ปัญหานี้ถูกต้อง แต่ฉันอยากจะขอให้คุณอ่านคำตอบที่จัดทำโดย templatetypedef ดังในคำตอบของ templatetypedef คำตอบที่เป็นไปได้ทั้งหมดจะกล่าวถึงรวมถึงคำตอบที่โพสต์โดย Rohit โซลูชันของ Rohit คือการนำโซลูชันสุดท้ายไปใช้กับ O (n) โดยใช้การเขียนโปรแกรมแบบไดนามิกที่กล่าวถึงในคำตอบที่จัดทำโดย templatetypedef
nits.kk

1
สมมติว่าอาร์เรย์ของคุณคือ int A [] = {5, 4, 6, 7, 6, 3, 2, 5}; จากนั้นตามตรรกะของคุณคุณจะซื้อที่ดัชนี 6 แล้วขายที่ดัชนี 3 ซึ่งผิด คุณไม่สามารถขายได้ในอดีต ดัชนีขายต้องมากกว่าดัชนีซื้อ
พัฒนา

1
วิธีแก้ปัญหาข้างต้น "เกือบ" ถูกต้อง แต่จะพิมพ์ดัชนีขั้นต่ำที่แน่นอนแทนดัชนีของราคา "ซื้อ" ในการแก้ไขคุณต้องมีตัวแปรอื่นพูดว่า minBuyIndex ซึ่งคุณอัปเดตเฉพาะในบล็อก "if (profit> maxProfit)" แล้วพิมพ์
javabrew

1

ฉันได้หาวิธีง่ายๆ - โค้ดเป็นแบบอธิบายตัวเองได้มากกว่า เป็นหนึ่งในคำถามเกี่ยวกับการเขียนโปรแกรมแบบไดนามิก

รหัสไม่ดูแลการตรวจสอบข้อผิดพลาดและขอบกรณี เป็นเพียงตัวอย่างเพื่อให้แนวคิดของตรรกะพื้นฐานในการแก้ปัญหา

namespace MaxProfitForSharePrice
{
    class MaxProfitForSharePrice
    {
        private static int findMax(int a, int b)
        {
            return a > b ? a : b;
        }

        private static void GetMaxProfit(int[] sharePrices)
        {
            int minSharePrice = sharePrices[0], maxSharePrice = 0, MaxProft = 0;
            int shareBuyValue = sharePrices[0], shareSellValue = sharePrices[0];

            for (int i = 0; i < sharePrices.Length; i++)
            {
                if (sharePrices[i] < minSharePrice )
                {
                    minSharePrice = sharePrices[i];
                    // if we update the min value of share, we need to reset the Max value as 
                    // we can only do this transaction in-sequence. We need to buy first and then only we can sell.
                    maxSharePrice = 0; 
                }
                else 
                {
                    maxSharePrice = sharePrices[i];
                }

                // We are checking if max and min share value of stock are going to
                // give us better profit compare to the previously stored one, then store those share values.
                if (MaxProft < (maxSharePrice - minSharePrice))
                {
                    shareBuyValue = minSharePrice;
                    shareSellValue = maxSharePrice;
                }

                MaxProft = findMax(MaxProft, maxSharePrice - minSharePrice);
            }

            Console.WriteLine("Buy stock at ${0} and sell at ${1}, maximum profit can be earned ${2}.", shareBuyValue, shareSellValue, MaxProft);
        }

        static void Main(string[] args)
        {
           int[] sampleArray = new int[] { 1, 3, 4, 1, 1, 2, 11 };
           GetMaxProfit(sampleArray);
            Console.ReadLine();
        }
    }
}

1
public static double maxProfit(double [] stockPrices)
    {
        double initIndex = 0, finalIndex = 0;

        double tempProfit = list[1] - list[0];
        double maxSum = tempProfit;
        double maxEndPoint = tempProfit;


        for(int i = 1 ;i<list.length;i++)
        {
            tempProfit = list[ i ] - list[i - 1];;

            if(maxEndPoint < 0)
            {
                maxEndPoint = tempProfit;
                initIndex = i;
            }
            else
            {
                maxEndPoint += tempProfit;
            }

            if(maxSum <= maxEndPoint)
            {
                maxSum = maxEndPoint ;
                finalIndex = i;
            }
        }
        System.out.println(initIndex + " " + finalIndex);
        return maxSum;

    }

นี่คือทางออกของฉัน แก้ไขอัลกอริทึมลำดับย่อยสูงสุด แก้ปัญหาใน O (n) ฉันคิดว่ามันไม่สามารถทำได้เร็วกว่านี้


1

นี่เป็นปัญหาที่น่าสนใจเพราะดูเหมือนจะยาก แต่การคิดอย่างรอบคอบจะให้วิธีแก้ปัญหาที่หรูหราและลดทอนลง

ดังที่ได้กล่าวมาแล้วมันสามารถแก้ไขได้อย่างดุร้ายในเวลา O (N ^ 2) สำหรับแต่ละรายการในอาร์เรย์ (หรือรายการ) ให้วนซ้ำรายการก่อนหน้าทั้งหมดเพื่อรับค่าต่ำสุดหรือสูงสุดขึ้นอยู่กับว่าปัญหาคือการหากำไรหรือขาดทุนมากที่สุด

ต่อไปนี้เป็นวิธีคิดวิธีแก้ปัญหาใน O (N): แต่ละรายการแทนค่าสูงสุด (หรือต่ำสุด) ใหม่ที่เป็นไปได้ จากนั้นสิ่งที่เราต้องทำคือบันทึกนาทีก่อนหน้า (หรือสูงสุด) และเปรียบเทียบความแตกต่างกับกระแสและค่าต่ำสุดก่อนหน้า (หรือสูงสุด) peasy ง่าย

นี่คือรหัสใน Java เป็นการทดสอบ JUnit:

import org.junit.Test;

public class MaxDiffOverSeriesProblem {

    @Test
    public void test1() {
        int[] testArr = new int[]{100, 80, 70, 65, 95, 120, 150, 75, 95, 100, 110, 120, 90, 80, 85, 90};

        System.out.println("maxLoss: " + calculateMaxLossOverSeries(testArr) + ", maxGain: " + calculateMaxGainOverSeries(testArr));
    }

    private int calculateMaxLossOverSeries(int[] arr) {
        int maxLoss = 0;

        int idxMax = 0;
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] > arr[idxMax]) {
                idxMax = i;
            }

            if (arr[idxMax] - arr[i] > maxLoss) {
                maxLoss = arr[idxMax] - arr[i];
            }           
        }

        return maxLoss;
    }

    private int calculateMaxGainOverSeries(int[] arr) {
        int maxGain = 0;

        int idxMin = 0;
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] < arr[idxMin]) {
                idxMin = i;
            }

            if (arr[i] - arr[idxMin] > maxGain) {
                maxGain = arr[i] - arr[idxMin];
            }           
        }

        return maxGain;
    }

}

ในกรณีของการคำนวณการสูญเสียที่ยิ่งใหญ่ที่สุดเราจะติดตามค่าสูงสุดในรายการ (ราคาซื้อ) จนถึงรายการปัจจุบัน จากนั้นเราจะคำนวณความแตกต่างระหว่างค่าสูงสุดและรายการปัจจุบัน ถ้าสูงสุด - ปัจจุบัน> maxLoss เราจะคงความแตกต่างนี้ไว้เป็น maxLoss ใหม่ เนื่องจากดัชนีสูงสุดได้รับการประกันว่าจะน้อยกว่าดัชนีปัจจุบันเราจึงรับประกันได้ว่าวันที่ "ซื้อ" น้อยกว่าวันที่ "ขาย"

ในกรณีของการคำนวณกำไรที่มากที่สุดทุกอย่างจะกลับกัน เราติดตามนาทีในรายการจนถึงรายการปัจจุบัน เราคำนวณความแตกต่างระหว่างค่าต่ำสุดและรายการปัจจุบัน (ย้อนกลับลำดับในการลบ) หาก current - min> maxGain เราจะคงความแตกต่างนี้ไว้เป็น maxGain ใหม่ อีกครั้งดัชนีของ 'ซื้อ' (นาที) มาก่อนดัชนีปัจจุบัน ('ขาย')

เราจำเป็นต้องติดตาม maxGain (หรือ maxLoss) และดัชนีต่ำสุดหรือสูงสุดเท่านั้น แต่ไม่ใช่ทั้งสองอย่างและเราไม่จำเป็นต้องเปรียบเทียบดัชนีเพื่อตรวจสอบว่า 'ซื้อ' น้อยกว่า 'ขาย' เนื่องจากเรา รับสิ่งนี้อย่างเป็นธรรมชาติ


1

กำไรสูงสุดขายครั้งเดียวโซลูชัน O (n)

function stocks_n(price_list){
    var maxDif=0, min=price_list[0]

    for (var i in price_list){
        p = price_list[i];
        if (p<min)
            min=p
        else if (p-min>maxDif)
                maxDif=p-min;
   }

    return maxDif
}

นี่คือโครงการที่ทำการทดสอบความซับซ้อนของเวลาบนแนวทาง o (N) vs o (n ^ 2) บนชุดข้อมูลแบบสุ่มใน 100k ints O (n ^ 2) ใช้เวลา 2 วินาทีในขณะที่ O (n) ใช้เวลา 0.01 วินาที

https://github.com/gulakov/complexity.js

function stocks_n2(ps){
    for (maxDif=0,i=_i=0;p=ps[i++];i=_i++)
        for (;p2=ps[i++];)
            if (p2-p>maxDif)
                maxDif=p2-p
    return maxDif
}

นี่คือวิธีการที่ช้ากว่า o (n ^ 2) ที่วนซ้ำตลอดเวลาที่เหลือของแต่ละวันแบบวนซ้ำสองรอบ


1

คำตอบที่ได้รับการโหวตสูงสุดไม่อนุญาตในกรณีที่กำไรสูงสุดเป็นลบและควรแก้ไขเพื่อให้สามารถทำได้ในกรณีดังกล่าว เราสามารถทำได้โดย จำกัด ช่วงของลูปเป็น (len (a) - 1) และเปลี่ยนวิธีกำหนดผลกำไรโดยการเปลี่ยนดัชนีทีละรายการ

def singSellProfit(a):
profit = -max(a)
low = a[0]

for i in range(len(a) - 1):
    low = min(low, a[i])
    profit = max(profit, a[i + 1] - low)
return profit

เปรียบเทียบเวอร์ชันของฟังก์ชันนี้กับฟังก์ชันก่อนหน้าสำหรับอาร์เรย์:

s = [19,11,10,8,5,2]

singSellProfit(s)
-1

DynamicProgrammingSingleSellProfit(s)
0

0
static void findmaxprofit(int[] stockvalues){
    int buy=0,sell=0,buyingpoint=0,sellingpoint=0,profit=0,currentprofit=0;
    int finalbuy=0,finalsell=0;
    if(stockvalues.length!=0){
        buy=stockvalues[0];
    }           
    for(int i=1;i<stockvalues.length;i++){  
        if(stockvalues[i]<buy&&i!=stockvalues.length-1){                
            buy=stockvalues[i];
            buyingpoint=i;
        }               
        else if(stockvalues[i]>buy){                
            sell=stockvalues[i];
            sellingpoint=i;
        }
        currentprofit=sell-buy;         
        if(profit<currentprofit&&sellingpoint>buyingpoint){             
            finalbuy=buy;
            finalsell=sell;
            profit=currentprofit;
        }

    }
    if(profit>0)
    System.out.println("Buy shares at "+finalbuy+" INR and Sell Shares "+finalsell+" INR and Profit of "+profit+" INR");
    else
        System.out.println("Don't do Share transacations today");
}

0

ความเป็นไปได้ในการกำหนดกำไรสูงสุดอาจเป็นการติดตามองค์ประกอบสูงสุดด้านซ้ายและด้านขวาในอาร์เรย์ที่ดัชนีแต่ละรายการในอาร์เรย์ เมื่อคุณทบทวนราคาหุ้นซ้ำแล้วซ้ำอีกในวันใดวันหนึ่งคุณจะทราบราคาต่ำสุดจนถึงวันนั้นและคุณจะทราบราคาสูงสุดหลังจาก (และรวมถึง) ในวันนั้นด้วย

ยกตัวอย่างเช่นขอกำหนดmin_arrและมีอาร์เรย์ที่ได้รับmax_arr arrดัชนีiในmin_arrจะเป็นองค์ประกอบขั้นต่ำarrสำหรับดัชนีทั้งหมด<= i(ด้านซ้ายของและรวมถึง i) ดัชนีiในmax_arrจะเป็นองค์ประกอบสูงสุดarrสำหรับดัชนีทั้งหมด>= i(ด้านขวาและรวมถึง i) จากนั้นคุณจะพบความแตกต่างสูงสุดระหว่างองค์ประกอบที่เกี่ยวข้องในmax_arrและ `` min_arr '':

def max_profit(arr)
   min_arr = []
   min_el = arr.first
   arr.each do |el|
       if el < min_el
           min_el = el
           min_arr << min_el
       else
           min_arr << min_el
       end
   end

   max_arr = []
   max_el = arr.last
   arr.reverse.each do |el|
       if el > max_el
           max_el = el
           max_arr.unshift(max_el)
       else
           max_arr.unshift(max_el)
       end

   end

   max_difference = max_arr.first - min_arr.first
   1.upto(arr.length-1) do |i|
        max_difference = max_arr[i] - min_arr[i] if max_difference < max_arr[i] - min_arr[i]  
   end

   return max_difference 
end

สิ่งนี้ควรทำงานในเวลา O (n) แต่ฉันเชื่อว่ามันใช้พื้นที่มาก


0

นี่คือความแตกต่างสูงสุดระหว่างสององค์ประกอบในอาร์เรย์และนี่คือทางออกของฉัน:

ความซับซ้อนของเวลา O (N) ความซับซ้อนของพื้นที่ O (1)

    int[] arr   =   {5, 4, 6 ,7 ,6 ,3 ,2, 5};

    int start   =   0;
    int end     =   0;
    int max     =   0;
    for(int i=1; i<arr.length; i++){
        int currMax =   arr[i] - arr[i-1];
        if(currMax>0){
            if((arr[i] -arr[start])>=currMax && ((arr[i] -arr[start])>=(arr[end] -arr[start]))){

                 end    =   i;
            }
            else if(currMax>(arr[i] -arr[start]) && currMax >(arr[end] - arr[start])){
                start   =   i-1;
                end =   i;
            }
        }
    }
    max =   arr[end] - arr[start];
    System.out.println("max: "+max+" start: "+start+" end: "+end);

0

หลังจากล้มเหลวในการสอบการเข้ารหัสสดสำหรับตำแหน่งวิศวกรโซลูชัน FB ฉันต้องแก้ปัญหานี้ในบรรยากาศที่สงบเย็นดังนั้นนี่คือ 2 เซนต์ของฉัน:

var max_profit = 0;
var stockPrices = [23,40,21,67,1,50,22,38,2,62];

var currentBestBuy = 0; 
var currentBestSell = 0;
var min = 0;

for(var i = 0;i < (stockPrices.length - 1) ; i++){
    if(( stockPrices[i + 1] - stockPrices[currentBestBuy] > max_profit) ){
        max_profit = stockPrices[i + 1] - stockPrices[currentBestBuy];
        currentBestSell = i + 1;  
    }
    if(stockPrices[i] < stockPrices[currentBestBuy]){
            min = i;
        }
    if( max_profit < stockPrices[i + 1] - stockPrices[min] ){
        max_profit = stockPrices[i + 1] - stockPrices[min];
        currentBestSell = i + 1;
        currentBestBuy = min;
    }
}

console.log(currentBestBuy);
console.log(currentBestSell);
console.log(max_profit);

ไม่แนะนำให้ใช้รหัสคำตอบเท่านั้น
Pritam Banerjee

0

คำตอบเดียวที่ตอบคำถามจริงๆคือคำตอบของ @akash_magoon (และด้วยวิธีง่ายๆเช่นนี้!) แต่จะไม่ส่งคืนวัตถุที่ระบุในคำถาม ฉันปรับโครงสร้างใหม่เล็กน้อยและได้รับคำตอบใน PHP ที่ส่งคืนสิ่งที่ถาม:

function maximizeProfit(array $dailyPrices)
{
    $buyDay = $sellDay = $cheaperDay = $profit = 0;

    for ($today = 0; $today < count($dailyPrices); $today++) {
        if ($dailyPrices[$today] < $dailyPrices[$cheaperDay]) {
            $cheaperDay = $today;
        } elseif ($dailyPrices[$today] - $dailyPrices[$cheaperDay] > $profit) {
            $buyDay  = $cheaperDay;
            $sellDay = $today;
            $profit   = $dailyPrices[$today] - $dailyPrices[$cheaperDay];
        }
    }
    return [$buyDay, $sellDay];
}

0

วิธีแก้ปัญหาที่เรียบร้อย:

+ (int)maxProfit:(NSArray *)prices {
    int maxProfit = 0;

    int bestBuy = 0;
    int bestSell = 0;
    int currentBestBuy = 0;

    for (int i= 1; i < prices.count; i++) {
        int todayPrice = [prices[i] intValue];
        int bestBuyPrice = [prices[currentBestBuy] intValue];
        if (todayPrice < bestBuyPrice) {
            currentBestBuy = i;
            bestBuyPrice = todayPrice;
        }

        if (maxProfit < (todayPrice - bestBuyPrice)) {
            bestSell = i;
            bestBuy = currentBestBuy;
            maxProfit = (todayPrice - bestBuyPrice);
        }
    }

    NSLog(@"Buy Day : %d", bestBuy);
    NSLog(@"Sell Day : %d", bestSell);

    return maxProfit;
}

0
def get_max_profit(stock):
    p=stock[0]
    max_profit=0
    maxp=p
    minp=p
    for i in range(1,len(stock)):
        p=min(p,stock[i])
        profit=stock[i]-p
        if profit>max_profit:
            maxp=stock[i]
            minp=p
            max_profit=profit
    return minp,maxp,max_profit



stock_prices = [310,315,275,295,260,270,290,230,255,250]
print(get_max_profit(stock_prices))

โปรแกรมนี้ใน python3 สามารถกลับราคาซื้อและราคาขายที่จะเพิ่มกำไรคำนวณที่มีความซับซ้อนของ O (n)และความซับซ้อนอวกาศของ O (1)


0

นี่คือทางออกของฉัน

public static int maxProfit(List<Integer> in) {
    int min = in.get(0), max = 0;
    for(int i=0; i<in.size()-1;i++){

        min=Math.min(min, in.get(i));

        max = Math.max(in.get(i) - min, max);
     }

     return max;
 }
}

-1

สำหรับคำตอบทั้งหมดที่ติดตามองค์ประกอบต่ำสุดและสูงสุดคำตอบนั้นเป็นโซลูชัน O (n ^ 2) เนื่องจากในตอนท้ายจะต้องตรวจสอบว่าค่าสูงสุดเกิดขึ้นหลังจากค่าต่ำสุดหรือไม่ ในกรณีที่ไม่เป็นเช่นนั้นจำเป็นต้องมีการทำซ้ำเพิ่มเติมจนกว่าจะเป็นไปตามเงื่อนไขและสิ่งนี้ทำให้ O (n ^ 2) เป็นกรณีที่เลวร้ายที่สุด และหากคุณต้องการข้ามการทำซ้ำเพิ่มเติมก็ต้องใช้พื้นที่มากขึ้น ไม่ว่าจะด้วยวิธีใด no-no เมื่อเทียบกับโซลูชันการเขียนโปรแกรมแบบไดนามิก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.