Monday, March 21, 2022

Leetcode discuss: 2096. Step-By-Step Directions From a Binary Tree Node to Another

 March 21, 2022

Here is the link. 

C# | Post order | Study discuss post | Solve TLE problem

March 21, 2022
Introduction
It is humble experience to learn how to write a post order traversal and learn how to record from node to root node path, and also apply conditional search by checking return value of recursive function. Recursive function design is challenge and then I started to look for working solution first.

Test case | Example
Input: root = [5,1,2,3,null,6,4], startValue = 3, destValue = 6
The path to found destValue 6 is "LR" instead of "RL", bottom up, post order, and it is the path from source node to root node.

I chose to use preorder traversal, build prefix string first but it ran into TLE error, Preorder practice | TLE error. Compared to preorder traversal, postorder traversal the path is not built for those failed paths so it is more efficient, much less string concatenation manipulation operations, only for valid path. No worry about TLE error. This lesson is learned so surprisingly and efficiently.

Post order traversal | Find path | C# StringBuilder
I just learned how to write a working solution using discuss post, and then learn a few others as well.

The following C# code passes online judge.

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

namespace _2096_step_by_step
{
    class Program
    {
        public class TreeNode {
          public int val;
          public TreeNode left;
          public TreeNode right;
          public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) {
              this.val = val;
              this.left = left;
              this.right = right;
          }
        }

        static void Main(string[] args)
        {
            var node5 = new TreeNode(5);
            node5.left = new TreeNode(1);
            node5.right = new TreeNode(2);
            node5.left.left = new TreeNode(3);
            node5.right.left = new TreeNode(6);
            node5.right.right = new TreeNode(4);

            var test = new Program();
            var start = test.GetDirections(node5, 3, 6);
            //var end = test.GetDirections(node5,6, 3);
        }

        /// <summary>
        /// March 21, 2022
        /// Find startValue using 'L','R','U'
        /// Find endValue using 'L','R','U'
        /// study code:
        /// https://leetcode.com/problems/step-by-step-directions-from-a-binary-tree-node-to-another/discuss/1617344/C-Solution
        /// </summary>
        /// <param name="root"></param>
        /// <param name="startValue"></param>
        /// <param name="destValue"></param>
        /// <returns></returns>
        public string GetDirections(TreeNode root, int startValue, int destValue)
        {
            var startPath = new StringBuilder();
            var destPath = new StringBuilder();

            runPostorderSearch(root, startValue, startPath);
            runPostorderSearch(root, destValue, destPath);

            startPath = new StringBuilder(new string(startPath.ToString().Reverse().ToArray()));
            destPath = new StringBuilder(new string(destPath.ToString().Reverse().ToArray()));

            while (startPath.Length > 0 && destPath.Length > 0 && startPath[0] == destPath[0])
            {
                startPath.Remove(0, 1);
                destPath.Remove(0, 1);
            }

            for (int i = 0; i < startPath.Length; ++i)
            {
                if (startPath[i] == 'L' || startPath[i] == 'R')
                {
                    startPath[i] = 'U';
                }
            }

            return startPath.ToString() + destPath.ToString();
        }

        /// <summary>
        /// Lessons learned:
        /// preorder traversal, search left subtree, if not found then search right subtree.
        /// It is conditional search. Do not search right subtree unless search of left subtree fails
        /// Use bool value for preorder traversal
        /// reverse order - postorder - not preorder
        /// </summary>
        /// <param name="root"></param>
        /// <param name="value"></param>
        /// <param name="path"></param>
        /// <returns></returns>
        public bool runPostorderSearch(TreeNode root, int value, StringBuilder path)
        {
            if (root.val == value)
            {
                return true;
            }
            else if (root.left != null && runPostorderSearch(root.left, value, path))
            {
                path.Append("L"); // Do not assign new StringBuilder, change pointer address 
                return true;
            }
            else if (root.right != null && runPostorderSearch(root.right, value, path))
            {
                path.Append("R");
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}

Leetcode discuss: 2096. Step-By-Step Directions From a Binary Tree Node to Another

March 21, 2022

Here is the link. 

C# | Post order traversal | Solve TLE problem

March 21, 2022
Introduction
It is hard for me to solve TLE problem until I came cross a few of discuss post using C# and both of them applies post order traversal, only apply path concentation string manipulation for only one working path only. I just learned the importance to avoid unnecessary work, since I prepare for Meta phone screen, and I understand that it is so important to learn to solve TLE problem.

Algorithm analysis
I just copied the analysis from votrubac, and it is important to understand the tasks:

  1. Find paths to source value and dest value;
  2. Find common prefix, and then source path is replaced by "U" char always
  3. Dest path should be appended after removing those common prefix with source path.

Build directions for both start and destination from the root.
Say we get "LLRRL" and "LRR".
Remove common prefix path.
We remove "L", and now start direction is "LRRL", and destination - "RR"
Replace all steps in the start direction to "U" and add destination direction.
The result is "UUUU" + "RR".

Post order traversal | C# List | C# List API Insert | Bottom-up, always insert in front of the list
I just quickly made some modification of study code, and then code passes online judge.

Design requirement | check list for the player
Design requirement:
Only call C# List.Insert API for the path from root to node with val
In other words, do not build path for any other path in the binary tree. To find source node, it requires to traverse the whole tree worst case, but it only takes less than height of tree steps to build the actual path using 'L' or 'R'. Do not involve those nodes not in the path, or think about back tracking etc.

TLE error is very common in Leetcode 2096 online judge

Design recursive function with bool return value, so it is easy to check post order traversal return value; only call C# List.Insert API when the node is in the path from source node to root node, and also Insert at index position 0.

The following C# code passes online judge.

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

namespace _2096_step___by___step_post_order___TLE
{
    class Program
    {
        public class TreeNode {
          public int val;
          public TreeNode left;
          public TreeNode right;
          public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) {
              this.val = val;
              this.left = left;
              this.right = right;
          }
        }

        static void Main(string[] args)
        {
            var node5 = new TreeNode(5);
            node5.left = new TreeNode(1);
            node5.right = new TreeNode(2);
            node5.left.left = new TreeNode(3);
            node5.right.left = new TreeNode(6);
            node5.right.right = new TreeNode(4);

            var test = new Program();
            var start = test.GetDirections(node5, 3, 6);
            //var end = test.GetDirections(node5,6, 3);
        }

        /// <summary>
        /// March 21, 2022
        /// study code
        /// https://leetcode.com/problems/step-by-step-directions-from-a-binary-tree-node-to-another/discuss/1613921/C-Postorder-Travesal
        /// </summary>
        /// <param name="root"></param>
        /// <param name="startValue"></param>
        /// <param name="destValue"></param>
        /// <returns></returns>
        public string GetDirections(TreeNode root, int startValue, int destValue)
        {
            if (root == null)
                return string.Empty;

            var pathStart = new List<char>();
            var pathEnd = new List<char>();

            runPostOrderTraversal(root, startValue, pathStart);
            runPostOrderTraversal(root, destValue,  pathEnd);

            while (pathStart.Count > 0 && pathEnd.Count > 0 && pathStart[0] == pathEnd[0])
            {
                pathStart.RemoveAt(0);
                pathEnd.RemoveAt(0);
            }

            var sb = new StringBuilder();
            for (int i = 0; i < pathStart.Count; i++)
            {
                sb.Append('U');
            }
            for (int i = 0; i < pathEnd.Count; i++)
            {
                sb.Append(pathEnd[i]);
            }

            return sb.ToString();
        }

        /// Conditional traversal - check return value
        /// Design requirement:
        /// Only call C# List.Insert API for the path from root to node with val
        /// In other words, do not build path for any other path in the binary tree. 
        /// TLE error is very common in Leetcode 2096 online judge 
        private bool runPostOrderTraversal(TreeNode root, int val, List<char> path)
        {
            if (root == null)
            {
                return false;
            }

            if (root.val == val)
            {
                return true;
            }

            if (runPostOrderTraversal(root.left, val, path))
            {
                path.Insert(0, 'L'); // bottom up - so insert at 0 position make sense
                return true;
            }
            else if (runPostOrderTraversal(root.right, val, path))
            {
                path.Insert(0, 'R');
                return true;
            }

            return false;
        }
    }
}

Leetcode discuss: 1162. As Far from Land as Possible

March 21, 2022

Here is the link. 

C# | BFS | All land nodes into Queue | Search together

March 21, 2022
Introduction
The algorithm is to ask a water node's maximum distance from land node. The idea is to put all land nodes into a queue, and then apply BFS search to visit all water nodes. The steps to run into an empty queue is that the steps to find maximum water node in given matrix.

The following C# code passes online judge.

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

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

        /// <summary>
        /// code study
        /// https://leetcode.com/problems/as-far-from-land-as-possible/discuss/1096186/c
        /// </summary>
        /// <param name="grid"></param>
        /// <returns></returns>
        public int MaxDistance(int[][] grid)
        {
            if (grid == null || grid.Length == 0)
            {
                return 0;
            }

            int result = -1;
            var queue = new Queue<int[]>();
            var rows = grid.Length;
            var columns = grid[0].Length;

            var visited = new bool[rows, columns];

            var directionRow = new int[] { 0, 0, 1, -1 };
            var directionColumn = new int[] { 1, -1, 0, 0 };

            for (int i = 0; i < rows; i++)
            {
                for (int j = 0; j < columns; j++)
                {
                    if (grid[i][j] == 1)
                    {
                        queue.Enqueue(new int[] { i, j });
                    }
                }
            }

            // apply BFS search
            while (queue.Count > 0)
            {
                var count = queue.Count;

                result++;

                while (count > 0)
                {
                    var current = queue.Dequeue();

                    for (int direction = 0; direction < 4; direction++)
                    {
                        int nextRow = current[0] + directionRow[direction];
                        var nextColumn = current[1] + directionColumn[direction];

                        if (nextRow > -1 && nextRow < grid.Length && nextColumn > -1 && nextColumn < grid[0].Length && !visited[nextRow, nextColumn] && grid[nextRow][nextColumn] == 0)
                        {
                            queue.Enqueue(new int[] { nextRow, nextColumn });
                            visited[nextRow, nextColumn] = true;
                        }
                    }

                    count--;
                }
            }

            return result == 0 ? -1 : result;
        }
    }
}


Sunday, March 20, 2022

Leetcode solution: One more to follow

 Here is the link. 

Leetcode algorithm study:

 https://leetcode.com/Poorvank/

Leetcode algorithm study: 979. Distribute Coins in Binary Tree

 March 20, 2022

Here is the link. 

C++ with picture, post-order traversal

We traverse childs first (post-order traversal), and return the ballance of coins. For example, if we get '+3' from the left child, that means that the left subtree has 3 extra coins to move out. If we get '-1' from the right child, we need to move 1 coin in. So, we increase the number of moves by 4 (3 moves out left + 1 moves in right). We then return the final ballance: r->val (coins in the root) + 3 (left) + (-1) (right) - 1 (keep one coin for the root).
image

int traverse(TreeNode* r, int &moves) {
  if (r == nullptr) return 0;
  int left = traverse(r->left, moves), right = traverse(r->right, moves);
  moves += abs(left) + abs(right);
  return r->val + left + right - 1;
}
int distributeCoins(TreeNode* r, int moves = 0) {
  traverse(r, moves);
  return moves;
}

If we can modify values of tree nodes, we can store the balance in nodes, and use the return value to accumulate the number of moves. This way we can get rid of the helper method. The solution below is courtesy of Lee:

int distributeCoins(TreeNode* r, TreeNode* p = nullptr) {
  if (r == nullptr) return 0;
  int res = distributeCoins(r->left, r) + distributeCoins(r->right, r);
  if (p != nullptr) p->val += r->val - 1;
  return res + abs(r->val - 1);
}

March 2022 stock performance: Yahoo -> Finance

 March 20, 2022

Introduction

I took over 30 minutes to put together a portfolio, so I can easily track my progress in my stock investment this March 2022. 

Total gain: $3076.52 | Continue to track my progress

I like to track my progress and then I can improve my stock investment thinking process. 


SABR stock | March 7 $7.9/ share | Possible gain $11,000 US dollars



Leetcode algorithm study: 983. Minimum Cost For Tickets

 March 20, 2022

I like to learn from this Google engineer how to solve 10 algorithms. 

This is the first one. 983. Minimum Cost For Tickets

Here is the link.

Two DP solutions with pictures

The higher is the bar, the more you're expected to use dynamic programming (DP) during an interview. This technique requires a lot of practice to grasp; if you've mastered the recursion, DP is the next level.

This problem appeared on LeetCode weekly contest #121, and it's a good problem to practice the DP thinking.

Intuition

For each travel day, we can buy a one-day ticket, or use 7-day or 30-day pass as if we would have purchased it 7 or 30 days ago. We need to track rolling costs for at least 30 days back, and use them to pick the cheapest option for the next travel day.

Here, we can use two approaches: track cost for all calendar days, or process only travel days. The first approach is simpler to implement, but it's slower. Since the problem is limited to one calendar year, it does not make much of a difference; for a generalized problem I would recommend the second approach.

1. Track calendar days

We track the minimum cost for all calendar days in dp. For non-travel days, the cost stays the same as for the previous day. For travel days, it's a minimum of yesterday's cost plus single-day ticket, or cost for 8 days ago plus 7-day pass, or cost 31 days ago plus 30-day pass.

image

int mincostTickets(vector<int>& days, vector<int>& costs) {
  unordered_set<int> travel(begin(days), end(days));
  int dp[366] = {};
  for (int i = 1; i < 366; ++i) {
    if (travel.find(i) == travel.end()) dp[i] = dp[i - 1];
    else dp[i] = min({ dp[i - 1] + costs[0], dp[max(0, i - 7)] + costs[1], dp[max(0, i - 30)] + costs[2]});
  }
  return dp[365];
}

Optimizations

In the previous solution, we store cost for all calendar days. However, since we only look 30 days back, we can just store the cost for last 30 days in a rolling array.

In addition, we can only look at calendar days within our first and last travel dates, as @zengxinhai suggested.

int mincostTickets(vector<int>& days, vector<int>& costs) {
  unordered_set<int> travel(begin(days), end(days));
  int dp[30] = {};
  for (int i = days.front(); i <= days.back(); ++i) {
    if (travel.find(i) == travel.end()) dp[i % 30] = dp[(i - 1) % 30];
    else dp[i % 30] = min({ dp[(i - 1) % 30] + costs[0],
        dp[max(0, i - 7) % 30] + costs[1], dp[max(0, i - 30) % 30] + costs[2] });
  }
  return dp[days.back() % 30];
}

Complexity analysis

  • Time Complexity: O(N), where N is the number of calendar days.
  • Space Complexity: O(N) or O(31) for the optimized solution. Stricter, it's a maximum duration among all pass types.

2. Track travel days

We track the minimum cost for each travel day. We process only travel days and store {day, cost} for 7-and 30-day passes in the last7 and last30 queues. After a pass 'expires', we remove it from the queue. This way, our queues only contains travel days for the last 7 and 30 days, and the cheapest pass prices are in the front of the queues.

image

int mincostTickets(vector<int>& days, vector<int>& costs, int cost = 0) {
  queue<pair<int, int>> last7, last30;
  for (auto d : days) {
    while (!last7.empty() && last7.front().first + 7 <= d) last7.pop();
    while (!last30.empty() && last30.front().first + 30 <= d) last30.pop();
    last7.push({ d, cost + costs[1] });
    last30.push({ d, cost + costs[2] });
    cost = min({ cost + costs[0], last7.front().second, last30.front().second });
  }
  return cost;
}

Complexity analysis

  • Time Complexity: O(n), where n is the number of travel days.
  • Space Complexity: O(38). Stricter, it's a sum of duration for all pass types (1 + 7 + 30 in our case).

Three ideas to spend time on Leetcode algorithms

 March 20, 2022

Introduction

I like to find three good ideas to work on Leetcode algorithms. I only have less than 200 submission last 12 months, and I like to have some ideas to get those valuable experience who has more than 1500 submissions last 12 months. 

Three ideas to work on | Leetcode algorithms

Leetcode has better product features to help users to learn from other player's solution. One of features is to list all solution discuss posted by a player. 

I also start to use list product feature, and I read more often solution written for premium users. 


Google | Software Engineer | Leetcode solutions |

March 20, 2022

Introduction

I plan to spend a few hours to go over 20 algorithms. How to choose 20 algorithms to work on? I like to read solutions written by this Google engineer. 

Here is the link. 


Wechat group: My thoughts as an investor | Good timing to purchase AAL 30% rebound | Chase high price, not ready to hold for two weeks

March 20, 2022

I like to document my experience as an investor of AAL this March. I should have learned better to invest at least for two weeks, but I have chased three times for 20 cents gain on 1500 shares, 1000 shares.

 陈建敏 15:59

陈建敏 21:43

今天和@雨果 @一粒沙 打了网球。二个多小时,打了网球双打。和雨果讨论股票,联系到网球技术动作,双打战略战术。我双打打了几百上千场,谢萍只打了五次。雨果说我很会用战术,把球打给谢萍而不是雨果。

陈建敏 21:45

我今天讲的就是这二个星期很多股票涨了30%. 但是我作为群主,没有战术,最低点买了1500股AAL,最后短线,追高,应该买了不动,二个星期回报是7000美金。我只有1400美金。

陈建敏 21:46

陈建敏 21:46

陈建敏 21:46

陈建敏 21:47

陈建敏 21:48

谁都不知道底在哪里? 只有冒险去试。

陈建敏 21:50

在投资小群,大家要提高心理素质,提高投资经验和体会,一定要走出虚拟网络世界,和朋友一起切磋,一起打网球,一起跑步,一起约了打电话,通过交流,才能进步。网球运动通过陪练,拉球,打比赛,一场一场球一起练习,提醒配合,慢慢提高。

陈建敏 21:51

我自己水平不行,现在去追高这些股票,psfe, sabr, 都是没有仔细考虑清楚。

陈建敏 21:51

下定决心投AAL, 回报可以休息几个月。

陈建敏 21:52

我可以投更多资金,seekingAlpha 文章出来比较早。

陈建敏 21:54

今天Hugo反复提醒谢萍双打要打给我,不要打给我的搭档,一个加拿大小伙子,他技术很高。

陈建敏 21:55

投资股票,要建立一个自己团队很艰难,像一个双打队伍,要一起出去打比赛,一起练球,反复提醒搭档,战术。不厌其烦。

陈建敏 21:57

我今年第一次和付英凤通电话,二年没有通电话。投资股票,要在群里找搭档,不容易。互相信任,一起成长。

陈建敏 22:01

需要犯错,总结,要冒险。底部很难找。大跌之后散户心理恢复和调节,很不容易。

陈建敏 22:02

最近好多股票反弹30%, 有的只有四天。

Saturday, March 19, 2022

Stock youtube channel: 猫姐美股投资

3月20日美股一周复盘,满仓cash周三盘中终于进场,实时同步会员;美股下周怎么走?交易要义分享;标普500指数SPX,纳斯达克指数NDX,罗素IWM,ARKK方舟基金;美元美国

March 19, 2022

Here is the link.

3月16日周三盘中提示会员中线仓位进场,当天过夜多仓达两成;周四过夜仓位接近四成。 上周末周播公开复盘视频里我们强调1,美股历史上不会被“外事”即战争影响太多,股灾从来不因外事引起。2,全球股市狂跌,和俺2012年在纽交所的一次经历类似,这反而是美股的机会。本周我们的重大任务就是中线仓位进场布局,小伙伴们做的都太赞啦,九宫都不够放,一起精进!


AAL stock: Need to learn how to invest at the bottom of price last two months

 March 19, 2022

Introduction

It is tough to learn how to cut loss in January to invest AAL stock 300 shares, and then it is also challenge for me to get back to invest at bottom price $12.95/ share with 1500 shares purchase. I was scared to hold those shares last two weeks, instead I chased the stock when AAL stock price rebounded from $12.95 to $16.50. 

My positions | Learn how to hold positions next time

If I hold those 1800 shares of AAL from $12.95/ share, then I will have near $7000 dollars gain. In fact, I chose to trade and have gain around $1474 dollars. It is not easy to tell what my mistake is. 

It is learning experience. I have to buy and hold at least two weeks, and let market fully rebound on this airline stock AAL. 





Value investing: How to buy low and sell high?

 March 19, 2022

Google search those keywords:

  1. 非经济性利空
  2. 基本面信心不足
  3. 过度情绪性反应
  4. 情绪管控
  5. 筹码轻易被洗出
  6. 不断追高
  7. 90%投资人
  8. 需要做好情绪控管
  9. 江国中