Monday, April 25, 2022

Leetcode discuss: 324. Wiggle Sort II

April 25, 2022

Here is the link. 

C# | Find median value first, wiggle solution - even index, any number smaller than median

April 25, 2022
Introduction
It is interesting journay to come cross this algorithm. I am preparing meta onsite in May 2022, and then I decided to go over top voted algorithms by StefanPhchmann.

Analysis by StefanPhchmann | My analysis
Explanation
Assuming that there is O(N) time complexity to find nth_element.

Three-way partitioning
Let me introduce three-way partitioning to arrange the numbers so that those larger than the median come first, then those equal to the median come next, and then those smaller than the median come last.

Let's say nums is [10,11,...,19]. Then after nth_element and ordinary partitioning, we might have this (15 is my median):

index: 0 1 2 3 4 5 6 7 8 9
number: 18 17 19 16 15 11 14 10 13 12
I rewire it so that the first spot has index 5, the second spot has index 0, etc, so that I might get this instead:

index: 5 0 6 1 7 2 8 3 9 4
number: 11 18 14 17 10 19 13 16 12 15
And 11 18 14 17 10 19 13 16 12 15 is perfectly wiggly. And the whole partitioning-to-wiggly-arrangement (everything after finding the median) only takes O(n) time and O(1) space.

Leetcode C# discuss post | Study code | Figure out time complexity of partition
I copied one of C# discuss post, and then tried to understand the partition algorithm time complexity, can it be O(N) which is better than O(NlogN) - sorting algorithm? Based on quick select algorithm and it's worst case is with time complexity O(N^2), I guessed that the partition algorithm cannot be O(N).

The following C# code passes online judge.

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

namespace _324_Wiggle_sort_II
{
    class Program
    {
        static void Main(string[] args)
        {
            //[1,5,1,1,6,4]
            var test = new Program();
            var numbers = new int[] { 1, 5, 1, 1, 6, 4 };
            test.WiggleSort(numbers);
        }

        /// <summary>
        /// code review: 
        /// </summary>
        /// <param name="nums"></param>
        public void WiggleSort(int[] nums)
        {
            int medianIndex = nums.Length / 2;
            int median = FindKthLargest(nums, medianIndex);  // using O(N) time complexity

            int start = 0;
            int end = nums.Length - 1;
            int i = 0;
            int index = 0;

            // go over the example
            // understand how to put median vlaue in the middle of the array
            // even index - any number < median
            // odd index  - any number > median
            while (i <= end)
            {
                index = newIndex(nums, i);
                if (nums[index] > median)
                {
                    swap(nums, newIndex(nums, start), index);
                    start++;
                    i++;
                }
                else if (nums[index] < median)
                {
                    swap(nums, newIndex(nums, end), index);
                    end--;
                }
                else
                {
                    i++;
                }
            }
        }

        private int newIndex(int[] nums, int index)
        {
            return (1 + 2 * index) % (nums.Length | 1);
        }

        /// <summary>
        /// Code review - April 25, 2022
        /// Using time complexity O(N) to find kth largest element 
        /// </summary>
        /// <param name="nums"></param>
        /// <param name="k"></param>
        /// <returns></returns>
        public int FindKthLargest(int[] nums, int k)
        {
            int start = 0;
            int pivot = start;

            int end = nums.Length - 1;
            int index = k - 1;

            while (start < end)
            {
                pivot = partition(nums, start, end); 

                if (pivot < index)  // ? 
                {
                    start = pivot + 1;
                }
                else if (pivot > index)
                {
                    end = pivot - 1;
                }
                else
                {
                    return nums[pivot];
                }
            }

            return nums[start];
        }

        /// <summary>
        /// code review:
        /// April 25, 2022
        /// </summary>
        /// <param name="nums"></param>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <returns></returns>
        private int partition(int[] nums, int start, int end)
        {
            int pivot = start;
            while (start <= end)
            {
                // Find two numbers to swap, nums[start] > nums[pivot], and nums[end] < nums[pivot]
                while (start <= end && nums[pivot] <= nums[start])
                {
                    start++;
                }

                while (start <= end && nums[end] <= nums[pivot])
                {
                    end--;
                }

                if (end < start)
                {
                    break;
                }

                swap(nums, start, end);
            }

            swap(nums, end, pivot);
            return end;
        }

        private void swap(int[] nums, int i, int j)
        {
            int tmp = nums[i];

            nums[i] = nums[j];
            nums[j] = tmp;
        }
    }
}

Quick Select Algorithm

 

Quick Select Algorithm

Quick Select is a variation of the quicksort algorithm. It is an optimized way to find the kth smallest/largest element in an unsorted array.

Algorithm:

  • The partition part of the algorithm is same as that of quick sort.
  • After the partition function arranges the elements in list according to the pivot and returns the pivot_index, instead of recursing both sides of the pivot index, we recurse only for the part that contains our desired element

Time Complexity Analysis:

Worst case: Worst case occurs when we pick the largest/smallest element as pivot.

Leetcode discuss: 324. Wiggle Sort II

 April 25, 2022

Introduction

I just could not believe that this algorithm is such a great learning example for me to work hard. No matter it is to apply Meta job or investing as an active investor, there is always better opportunity to invest. Take less risk and invest long term. I chose to study StefanPhchomann's top voted algorithm, so it is the first algorithm I came cross today. 

Analysis | 324. Wiggle Sort II


Leetcode discuss: Another top voted algorithms | votrubac | Page 5

 


Leetcode discuss: Another top voted algorithms | votrubac | Page 4

 


Leetcode discuss: Another top voted algorithms | votrubac | Page 3

 


Leetcode discuss: StefanPochmann | Next 15 algorithms | Page 3 | top voted algorithms

April 25, 2022

I like to work on the following algorithms in next few days. 



 

Leetcode discuss: StefanPochmann | Next 15 algorithms | Page 2 | top voted algorithms

 April 25, 2022

I like to review the following 15 algorithms in next few days. 



Leetcode discuss: 36. Valid Sudoku

 April 25, 2022

C# | Validation process | HashSet

Here is the link. 

April 18, 2022
Introduction
The algorithm is to verify the given matrix is valid based on given digits and a few rules - check each row, each column, and then 8 small matrixes 3 x 3.

Rules to check

  1. Each row must contain the digits 1-9 without repetition.
  2. Each column must contain the digits 1-9 without repetition.
  3. Each of the nine 3 x 3 sub-boxes of the grid must contain the digits 1-9 without repetition.
    Note:
    A Sudoku board (partially filled) could be valid but is not necessarily solvable.
    Only the filled cells need to be validated according to the mentioned rules.

Test cases to verify

  1. If there is digit 0 in given matrix, it will return false; Since "123456789" specifies the digits in the hashset, 0 is not in HashSet.
  2. If there is a duplicate 1 in one row, then it will return false; By checking the given row, first '1' will invokde removal of '1' from HashSet, second one will find that '1' is not in HashSet, return false;
  3. Same as step 2 applies to one column, and one small matrix - 9 matrixes - define the left top corner, and then use count variable 0 - 9 to map the value of left top corner, and then two loops for row and col in size of 3 each.

I will study discuss post and write a few more solutions.

The following C# code passes online judge.

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

namespace _36_valid_sudoku
{
    class Program
    {
        static void Main(string[] args)
        {
            //[[".","8","7","6","5","4","3","2","1"],
            //["2",".",".",".",".",".",".",".","."],
            //["3",".",".",".",".",".",".",".","."],
            //["4",".",".",".",".",".",".",".","."],
            //["5",".",".",".",".",".",".",".","."],
            //["6",".",".",".",".",".",".",".","."],
            //["7",".",".",".",".",".",".",".","."],
            //["8",".",".",".",".",".",".",".","."],
            //["9",".",".",".",".",".",".",".","."]]
            var board = new char[9][];
            board[0] = new char[]{'.','8','7','6','5','4','3','2','1'};
            board[1] = new char[] { '2', '.', '.', '.', '.', '.', '.', '.', '.' };
            board[2] = new char[] { '3', '.', '.', '.', '.', '.', '.', '.', '.' };
            board[3] = new char[] { '4', '.', '.', '.', '.', '.', '.', '.', '.' };
            board[4] = new char[] { '5', '.', '.', '.', '.', '.', '.', '.', '.' };
            board[5] = new char[] { '6', '.', '.', '.', '.', '.', '.', '.', '.' };
            board[6] = new char[] { '7', '.', '.', '.', '.', '.', '.', '.', '.' };
            board[7] = new char[] { '8', '.', '.', '.', '.', '.', '.', '.', '.' };
            board[8] = new char[] { '9', '.', '.', '.', '.', '.', '.', '.', '.' };

            var result = IsValidSudoku(board);

            // Failed test case: 478 / 507 test cases passed.
            //[[".",".",".",".","5",".",".","1","."],
            //[".","4",".","3",".",".",".",".","."],
            //[".",".",".",".",".","3",".",".","1"],
            //["8",".",".",".",".",".",".","2","."],
            //[".",".","2",".","7",".",".",".","."],
            //[".","1","5",".",".",".",".",".","."],
            //[".",".",".",".",".","2",".",".","."],
            //[".","2",".","9",".",".",".",".","."],
            //[".",".","4",".",".",".",".",".","."]]
        }

        /// <summary>
        /// code review on April 18, 2022
        /// rules to follow:
        /// 1. Each row must contain the digits 1-9 without repetition.
        /// 2. Each column must contain the digits 1-9 without repetition.
        /// 3. Each of the nine 3 x 3 sub-boxes of the grid must contain the digits 1-9 without repetition.
        /// Note:
        /// A Sudoku board (partially filled) could be valid but is not necessarily solvable.
        /// Only the filled cells need to be validated according to the mentioned rules.
        /// </summary>
        /// <param name="board"></param>
        /// <returns></returns>
        public static bool IsValidSudoku(char[][] board)
        {
            if (board == null || board.Length != 9 || board[0].Length != 9)
                return false;

            var rows = board.Length;
            var columns = board[0].Length;
            var digits = "123456789".ToCharArray();
            for (int row = 0; row < 9; row++)
            {
                var set = new HashSet<char>(digits);
                for (int col = 0; col < 9; col++)
                {
                    var c = board[row][col];
                    if (c != '.' && !set.Contains(c))
                    {
                        return false; 
                    }

                    set.Remove(c);
                }               
            }

            for (int col = 0; col < 9; col++)
            {
                var set = new HashSet<char>(digits);
                for (int row = 0; row < 9; row++)
                {
                    var c = board[row][col];
                    if (c != '.' && !set.Contains(c))
                    {
                        return false; 
                    }

                    set.Remove(c);
                }
            }
            
            // check 3 * 3 matrix 
            // first row - 3 corners, next is fourth row, next is 7th row
            for (int count = 0; count < 9; count++)
            {
                var startRow = 3 * (count / 3);
                var startCol = 3 * (count % 3);

                var set = new HashSet<char>(digits);
                for (int row = 0; row < 3; row++)
                {
                    for (int col = 0; col < 3; col++)
                    {
                        var c = board[startRow + row][startCol + col];
                        if (c != '.' && !set.Contains(c))
                        {
                            return false;
                        }

                        set.Remove(c);
                    }
                }
            }

            return true;
        }        
    }
}

Leetcode discuss: 36. Valid Sudoku

 April 25, 2022

Here is the link. 

C# | Encode string - three ideas to map same row, same column, same 3 x 3 matrix

April 18, 2022
Introduction
I worked on the discuss post called 30 days to Meta onsite, and I chose to work on more algorithms on day 18, and I chose to work on top-voted algorithm by this profile Stefan Pochmann:

My practice
I just worked on the algorithm and put together a few test cases to make sure that my solution will work. Here is my first practice.

Next step | Learn how to write an elegant solution | prepare Meta onsite 4th time in May 2022
I also chose to learn to write an elegant solution based on Stefan Pochamann's solution.
How to encode a string to make sure that all digits in the same row will not have duplicate digit?
Hint: Inorder to separate row number from column number, row number is added after digit, whereas column integer is added before digit; In order to separate digit from row number or column number, put left and right parentheses around digit, for example, 1 -> (1), first row check rules - no duplicate, each digit in the first row will be (1)0, (2)0, ..., (9)0. But if the first row has digit 0, then it can not be detected and report error.

The challenge one is 3 x 3 matrix, how to identify 9 of those 3 x 3 matrixes? We can use left-top corner as start position to uniquely identify.
So first row to thir row, first column to third column 3 x 3 matrix
'.','8','7',
'2','.','.'
'3',',','.'
Encode those chars which are not '.'
0(8)0
0(7)0
0(2)0
0(3)0
So it is easy to tell that there is no duplicated char to represent digit from 1 to 9.

It is easy to figure out how to encode second row of 3 x 3 matrix, second column, the right-top corner is (3, 3). All digits will be represented in 3(?)3, ? is to represent any digit from 1 to 9.

If one digit is added to same row or same column or same 3 x 3 matrix more than once, then second HashSet.Add API will return false. HashSet variable is defined as seen, so it is easy to check if seen.Add return value is false using expression:

!seen.Add(b + row)

It takes 10 minutes to write down the idea how to encode, and also coding is easy to write, since two loops, one hashset, C# HashSet.Add API has return value - true or fale to represent result.

If needed, it is easy to add logic checking to check digit is not 0, and it is one of digits from 1 to 9.

My algorithms to work on | Stefan Pochamann's solution
It is hard for me to practice and have submission more than 800 submissions in one year. So I plan to work on top-voted algorithms written by Stefan Pochamann, and I think that it is important to be a good thinker like Stefan Pochamann to work on this algorithm using encoded string to quickly draft a solution in a few minutes.

image

The following C# code passes online judge.

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

namespace _36_valid_sudoku___encode_as_strings
{
    class Program
    {
        static void Main(string[] args)
        {
            //[[".","8","7","6","5","4","3","2","1"],
            //["2",".",".",".",".",".",".",".","."],
            //["3",".",".",".",".",".",".",".","."],
            //["4",".",".",".",".",".",".",".","."],
            //["5",".",".",".",".",".",".",".","."],
            //["6",".",".",".",".",".",".",".","."],
            //["7",".",".",".",".",".",".",".","."],
            //["8",".",".",".",".",".",".",".","."],
            //["9",".",".",".",".",".",".",".","."]]
            var board = new char[9][];
            board[0] = new char[] { '.', '8', '7', '6', '5', '4', '3', '2', '1' };
            board[1] = new char[] { '2', '.', '.', '.', '.', '.', '.', '.', '.' };
            board[2] = new char[] { '3', '.', '.', '.', '.', '.', '.', '.', '.' };
            board[3] = new char[] { '4', '.', '.', '.', '.', '.', '.', '.', '.' };
            board[4] = new char[] { '5', '.', '.', '.', '.', '.', '.', '.', '.' };
            board[5] = new char[] { '6', '.', '.', '.', '.', '.', '.', '.', '.' };
            board[6] = new char[] { '7', '.', '.', '.', '.', '.', '.', '.', '.' };
            board[7] = new char[] { '8', '.', '.', '.', '.', '.', '.', '.', '.' };
            board[8] = new char[] { '9', '.', '.', '.', '.', '.', '.', '.', '.' };

            var result = IsValidSudoku(board);
        }

        /// <summary>
        /// study code:
        /// https://leetcode.com/problems/valid-sudoku/discuss/15472/Short%2BSimple-Java-using-Strings
        /// Collect the set of things we see, encoded as strings. For example:
        /// '4' in row 7 is encoded as "(4)7".
        /// '4' in column 7 is encoded as "7(4)".
        /// '4' in the top-right block is encoded as "0(4)2".
        /// </summary>
        /// <param name="board"></param>
        /// <returns></returns>
        public static bool IsValidSudoku(char[][] board)
        {
            var seen = new HashSet<string>();

            for (int row = 0; row < 9; ++row)
            {
                for (int col = 0; col < 9; ++col)
                {
                    if (board[row][col] != '.')
                    {
                        var b = "(" + board[row][col] + ")";

                        if (!seen.Add(b + row) || !seen.Add(col + b) || !seen.Add(row / 3 + b + col / 3))
                        {
                            return false;
                        }
                    }
                }
            }

            return true;
        }
    }
}