Showing posts sorted by relevance for query min max heap. Sort by date Show all posts
Showing posts sorted by relevance for query min max heap. Sort by date Show all posts

Friday, June 3, 2022

Leetcode discuss: 2263. Make Array Non-decreasing or Non-increasing

Here is the link. 

C# | Quick learner | Maximum heap | Sorted<Tuple<int, int>> | Tip: Add twice

June 3, 2022
I like to be a quick learner. I could not figure out the trick by reading C++, Java code shared by votrubac. So I debugged and then figured out how to make the test case work.

Test case | {3, 2, 4, 5, 0} | Trick: Add twice into the max heap
I like to go over the test case {3, 2, 4, 5, 0} and explain why it takes 4 operations to make a non-decreasing order in reverse order.

I chose to use C# SortedSet<Tuple<int, int>> to simulate a maximum heap.
I tried to work on the above case step by step by myself. I am working on

cost(nums, -1)

First, last element in the array, index = 4, add it to Max heap, heap - new Tuple<int, int>, (0, 0)
Next, index = 3, value 5, add it to heap, (5, 1)
Next, index = 2, value 4, heap.Max.Item1 = 5 > 4, so result variable increments (5 - 4) = 1, remove (5, 1) from heap,
add (4, 2) to heap <- inside if statement

if (heap.Count > 0 && heap.Max.Item1 > current)
{
	result += heap.Max.Item1 - current;
    heap.Remove(heap.Max);
    heap.Add(new Tuple<int, int>(current, count++)); 
}

Next, it is to add (4, 3) to the heap by executing the statement outside if statement

// explain: why {3, 2, 4, 5, 0} reverse order, 4 will be added twice? 
heap.Add(new Tuple<int, int>(current, count++));

Mext. it is to add (2, 4) to the heap, and result += (4 - 2), result = 3, remove (4, 3) from heap
Last, it is to add (3, 5) to the heap, and result += (4 - 3), result = 4, remove (4, 2) from heap.

votrubac | Analysis
I just quickly copied the idea and analysis from votrubac in the following:

The greedy logic is quite tricky. We process numbers left-to-right, and put them into a max heap.

When the current number n is smaller than the largest so far m, we know that we need to do m - n adjustments.

Now, the tricky part. We can increase n and/or decrease m so that they become the same. But for the purpose of the greedy algorithm, we assume we decrease m all the way to be the same as n, and put the new value to the heap.

The following C# code passes online judge.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace _2263_make_array_non_decreasing
{
    class Program
    {
        static void Main(string[] args)
        {
            var result = ConvertArray(new int[]{3, 2, 4, 5, 0});
            Debug.Assert(result == 4); 
        }

        /// <summary>
        /// study code
        /// https://leetcode.com/problems/make-array-non-decreasing-or-non-increasing/discuss/2011905/Greedy
        /// </summary>
        /// <param name="nums"></param>
        /// <returns></returns>
        public static int ConvertArray(int[] nums)
        {
            return Math.Min(cost(nums), cost(nums, -1)); 
        }

        /// <summary>
        /// checklist:
        /// 1. Maximum heap
        /// 2. Tricky part - greedy - decrease m all the way to be the same as n, and put the new value to the heap
        /// </summary>
        /// <param name="nums"></param>
        /// <param name="d"></param>
        /// <returns></returns>
        private static int cost(int[] nums, int d = 1)
        {
            int result = 0;
            var heap = new SortedSet<Tuple<int, int>>();

            var length = nums.Length;
            var count = 0; 
            for (int i = (d == 1 ? 0 : length - 1); i < length && i >= 0; i += d)
            {
                var current = nums[i];

                if (heap.Count > 0 && heap.Max.Item1 > current)
                {
                    result += heap.Max.Item1 - current;
                    heap.Remove(heap.Max);
                    heap.Add(new Tuple<int, int>(current, count++)); 
                }

				// explain: why {3, 2, 4, 5, 0} reverse order, 4 will be added twice? 
                heap.Add(new Tuple<int, int>(current, count++));
            }

            return result; 
        }
    }
}

Friday, April 7, 2017

Walk through a small test case - median study

April 7, 2017


Problem statement: 


Add 1, 2, 3, 4, 5, and then keep tracking medium value to make sure that it is accessible using time complexity O(1).


Introduction


Julia teaches herself how to analyse step by step and get into the design of data structure this March 2017. First try, her thought thinking process looks naive from this blog but she likes to write down thought process, and continue to work on it. Life is much easy if Julia chooses to start baby step, reexamine things involved, small talks about every concept which should have been considered in the design process.

Julia likes to spend time on a test case rather than thinking about so many ideas/ articles/ practice she had worked on related to search median algorithm, that is a game to test memory. Instead Julia likes to show a better way, from her training of mathematics courses through her universities - SJTU, FAU math and computer science department, nothing can beat a small example and powerful message of problem solving, it is simple process but it brings out a good thought thinking process - maybe showing great mindset. Time is well-spent on the small test case. 

Baby step talk about data structure design 


Julia likes to practice this to get her familiar with heap concepts and also max heap and min heap.

Here we go.

First 1 is coming, put 1 into left side data structure, she is not sure what kind of data structure should be. 

The median is 1, no problem, just to get the first and only number in left side. 
Left side           Right side
1

And then, 2 is coming, Julia likes to put Right side.

Left side    Right side
1              2

Median is (1 + 2)/ 2 = 1.5

Now, 3 is coming, we have to decide 3 goes to which side, why? 

Binary search tree vs binary tree


1 2 3, 2 is the medium, we like to keep 2 at the top of data structure, first we decide to let 3 join which side, left or right? 

To allow first number 1 goes to left side, max heap is used for left side data structure. And there is implicit rule, left side data structure saves left half of the numbers, smaller one.

Repeat, middle element is the root of tree, no need to sort, binary tree, smaller half of numbers is in left side data structure. We can make it a max heap.

Rule 1: Left side data structure saves left half of the numbers - smaller ones


Because it is there is no need to sort everything which costs unnecessary time, using binary tree instead of binary search tree, to make median calculation be O(1), we like to keep the middle element at the root of binary tree. 

Left side - Max heap 


So, 1 is smallest value, go to left side, left data structure uses max heap. 
Left – 1
Right -   2

Right side - Min Heap

  
3, right side is min heap.

Extra rule - left size always not smaller than right side


Keep the left side’s size >= right side

Adjustment - heapify


Move 2 from right side to left side
Left side:  2 1   (starting from root node, and then level by level)

Using array to represent a heap


complete binary tree, 1 2 => node's value is smaller than child's value, swap => 2 1

Right side:  3
The median is 2, since left side’s nodes > right side’s node + 1

Next 4 is coming,  put 4 to right side

Left  side:  2  1
Right side: 3  4

The median is (2 + 3)/ 2 

Next 5 is coming, put 5 to right side because 5 is bigger than left side data structure - max heap's max value

Left side:   2  1
Right side: 3  4  5 

And then move 3 to left side:

Left side:
   2                   3
1   3     =>   1     2

Right side:
  5               4
4     =>    5

Actionable Item




On the other hand, seeing you find your way out of a difficult situation tells a lot about your character, how you perform under pressure, your ability to think on your feet and your problem solving skills.


1. Not thinking about an algorithm


Make things simpler for yourself. Write down an example on the board and think about just solving that particular instance of the problem by hand.

Small test case -> generalize it back into an algorithm form. 


People tend to bomb their first few sets of interviews. This is mostly because they don’t have sufficient practice with how to handle that pressure of solving an unknown question.


15 mocking interview - systematic way 



A note of thankfulness


Julia likes to write a small note to thank Brooklyn to help her on writing better on this blog's introduction section, who is a graduate of linguistic major from university of Victoria in 2015. Brooklyn gave her comment about blog writing in general, and she said that Julia writes very well now. 


Thursday, June 30, 2022

Leetcode discuss: 632. Smallest Range Covering Elements from K Lists

June 30, 2022

Here is the link. 

C# | Quick learner | Heap - Max and Min

June 30, 2022
Introduction
It is a hard level algorithm. What I did is to think about 5 minutes, and spent 5 minutes to read top-voted discuss post, and then moved on C# discuss post, studied and wrote my own. I like to cut short time to review and warmup hard level algorithms.

Hard level algorithms | My approach

1944
428
1096
847
1597

I spent three hours to go over 5 hard levle algorithms yesterday, and I only solved 847. Definitely I learned a few things. I have no clue what I learned yesterday about other 4 algorithms. But I am thinking about the new approach. Since I am approaching 700 algorithms solved, I have a lot of experience already. I try three 5-minutes approach.

Three 5-minutes approach on hard level algorithm

  1. Think about 5 minutes by myself - idea, design, and things to look into
  2. 5 minutes top voted discuss post - read some ideas shared by top-voted discuss post - Cut time short if needed, 5 minutes only
  3. Study one C# discuss post - 5 minutes
  4. Work on C# code rewrtie - Learn by doing.

Time complexity:
O((klogk)kL, k is total rows, L is total columns

The following C# code passes online judge.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace _632_smallest_range
{
    class Program
    {
        static void Main(string[] args)
        {
            var list = new List<IList<int>>();
            list.Add(new int[] { 4, 10, 15, 24, 26 }.ToList());
            list.Add(new int[] { 0, 9, 12, 20 }.ToList());
            list.Add(new int[] { 5, 18, 22, 30 }.ToList());

            var result = SmallestRange(list);
        }

        /// <summary>
        /// 1. 5 minutes to read top-voted discussion post
        /// 2. study code
        /// https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/discuss/1790188/Elegant-implementation-with-C-SortedSet-or-M-Log(min(len(M)))         
        /// </summary>
        /// <param name="nums"></param>
        /// <returns></returns>
        public static int[] SmallestRange(IList<IList<int>> nums) {
            var rows = nums.Count;
            var columns = nums[0].Count;
        
            // Tuple<int, int, int> design: value, row, column
            var heap = new SortedSet<Tuple<int, int, int>>();
        
            for(int row = 0; row < rows; row++)
            {
                heap.Add(new Tuple<int, int, int>(nums[row][0], row, 0));
            }
			
            var rangeMin = 0;
            var rangeMax = int.MaxValue;
        
            while(heap.Count > 0)
            {
                var min = heap.Min;
                var max = heap.Max;

                if(rangeMax - rangeMin > max.Item1 - min.Item1)
                {
                    rangeMin = min.Item1;
                    rangeMax = max.Item1;
                }

                heap.Remove(min);

                var nextRow = min.Item2;
                var nextColumn = min.Item3 + 1;

                if(nextColumn == nums[nextRow].Count)
                {
                    return new int[] {rangeMin, rangeMax};
                }            
                
                heap.Add(new Tuple<int, int, int>(nums[nextRow][nextColumn], nextRow, nextColumn));
            }

            return new int[0];
        }
    }
}

Sunday, April 24, 2022

Leetcode discuss: 295. Find Median from Data Stream

 April 24, 2022

Here is the link. 

C# | Minimum heap and maximum heap | SortedSet<Tuple<int, int>>

April 22, 2022
Maximum heap and minimum heap design
It is important to come out best time complexity using two heaps, one is maximum heap, one is minimum heap. So it is O(1) time complexity to find median value. That is most efficient way to find median value in a stream.

Prepare a check list

  1. Using C# Tuple<int, int> to build a minimum and maximum heap. The first one in Tuple is value, and the second one is the counter variable name value, which is helpful to keep duplicate value and make it unique since counter variable has identity value to increment one always.
  2. Design two heaps, one is maximum heap, one is minimum heap; The small half numbers are stored in maximum heap, using C# SortedSet<Tuple<int, int>>, called smallHalf;
  3. If the total count of numbers are even, then smallHalf and bigHalf has same number of integers; otherwise I choose to keep extra one into smallHalf always. It is better to change smallHalf to smallHalfPlusOne to remind myself, so it is easy for me to code, for example, add extra number to smallHalfPlusOne, get medium number from smallHalfPlusOne if total count is odd. In other words, easy to figure out, easy to avoid mistakes in the code.
  4. Variable naming: smallHalfPlusOne, bigHalf variables names are more meaningful compared to setLow, setHigh. Design minimum heap and maximum heap using C# SortedSet<Tuple<int, int>>.
  5. First step is to check which heap to add the incoming integer, next step is to move one number to another heap to maintain two heap's count's difference is at most one, and also if it is not equal, smallHalfPlusOne should have an extra integer.

Warmup for Meta onsite in May 2022
I chose to work on this algorithm to prepare Meta onsite in May 2022. I try to figure out what to learn from this practice. I wrote down my detail - 30 days to meta onsite here Day 18 - I chose to go over 15 algorithms from Stefan Pochmann.

The following C# code pass online judge.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace _295_Find_median_of_stream
{
    class Program
    {
        static void Main(string[] args)
        {
        }

        public class MedianFinder {

   private int counter = 0;

        private SortedSet<Tuple<int, int>> smallHalfPlusOne = new SortedSet<Tuple<int, int>>();
        private SortedSet<Tuple<int, int>> bigHalf = new SortedSet<Tuple<int, int>>();        

        public void AddNum(int num)
        {
             var newNum = new Tuple<int, int>( num, counter++ );            
            
            // Deal with empty minimum heap or maximum heap case 
            if ( smallHalfPlusOne.Count == 0 || newNum.Item1 < smallHalfPlusOne.Max.Item1)
            {
                smallHalfPlusOne.Add(newNum);
            }
            else
            {
                bigHalf.Add(newNum);
            }

            // There is more than one numbers in smallHalfPlusOne
            while (smallHalfPlusOne.Count > bigHalf.Count + 1)
            {
                bigHalf.Add(smallHalfPlusOne.Max);
                smallHalfPlusOne.Remove(smallHalfPlusOne.Max);
            }
            
            // bigHalf has more numbers than smallHalfPlus 
            while(bigHalf.Count > smallHalfPlusOne.Count)
            {
                // move the minimum number from setHigh to setLow. 
                smallHalfPlusOne.Add(bigHalf.Min);
                bigHalf.Remove(bigHalf.Min);                
            }                     
        }

        /// <summary>
        /// if minimum heap and maximum heap have same size, then medium is to get the average of those two values 
        /// </summary>
        /// <returns></returns>
        public double FindMedian()
        {
            if (smallHalfPlusOne.Count == 0)
            {
                return 0;
            }

            if (smallHalfPlusOne.Count == bigHalf.Count)
            {
                return (smallHalfPlusOne.Max.Item1 + bigHalf.Min.Item1) / 2d;
            }
            else
            {
                return smallHalfPlusOne.Max.Item1;
            }
        }
}


Monday, July 15, 2019

295. Find Median from Data Stream

Here is my practice sharing.

It is most challenging problem to solve since the optimal solution is to use two data structure, one is minimum heap, and the other one is maximum heap. I was asked to work on the algorithm in phone screen in March 2017.
It is time for me to review the algorithm again.
How to design minimum heap and maximum heap using C#?
Using C# SortedSet, and also define Comparer.
One tip to make median calcuation easy
It is a good idea to make minimum heap size is bigger than maximum heap size by 1 if total length is is odd.
public class MedianFinder {

    /** initialize your data structure here. */
    public MedianFinder() {
        
    }
    
    private int counter = 0;

        private SortedSet<int[]> setLow = new SortedSet<int[]>(
            Comparer<int[]>.Create((a, b) => a[0] == b[0] ? a[1] - b[1] : a[0] - b[0]));

        private SortedSet<int[]> setHigh = new SortedSet<int[]>(
            Comparer<int[]>.Create((a, b) => a[0] == b[0] ? a[1] - b[1] : a[0] - b[0]));
            
    public void AddNum(int num) {
        var newNum = new int[2] { num, counter++ };

            bool twoTreesSameSize = setLow.Count == setHigh.Count;          

            if (twoTreesSameSize)
            {
                if (setLow.Count == 0 || newNum[0] <= setLow.Max[0])
                {
                    setLow.Add(newNum);
                }
                else
                {
                    setHigh.Add(newNum);

                    // move the minimum number from setHigh to setLow. 
                    setLow.Add(setHigh.Min);
                    setHigh.Remove(setHigh.Min);
                }
            }
            else if (newNum[0] <= setLow.Max[0])
            {
                setLow.Add(newNum);

                // move the maximum number from setLow to setHigh
                setHigh.Add(setLow.Max);
                setLow.Remove(setLow.Max);
            }
            else
            {
                setHigh.Add(newNum);
            }
    }
    
    public double FindMedian() {
        if (setLow.Count == 0)
            {
                return 0;
            }

            if (setLow.Count == setHigh.Count)
            {
                return (setLow.Max[0] + setHigh.Min[0]) / 2d;
            }
            else
            {
                return setLow.Max[0];
            }
    }
}

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder obj = new MedianFinder();
 * obj.AddNum(num);
 * double param_2 = obj.FindMedian();
 */