Tuesday, June 21, 2022

Leetcode discuss: 947. Most Stones Removed with Same Row or Column

June 21, 2022

Here is the article. 


C# | Quick learner | Union find | DFS | 7 solutions | Top players -> improvements

June 13, 2022
It is hard to be a master of union find and DFS algorithm. I like to share my quick learning of C# solution after I chose to study various player solutions in weekly contest 112.

Also, I studied solutions written by various players, in weekly contest 112, ranking No. 5, 8, 12, 13, 16, 40, 58, 117, 176. The goal of my study is to read a lot of solutions, and learn to write a good one and enjoy the study. I did study those player code in 2019, and after three years, I like to share my C# solution as well.

I think that it is a good idea to put together 7 solutions in one discuss post, so that I can enjoy reading various ideas one time in C# programming language. After I read those ideas to write DFS or union find algorithms, I can expedite my thought process and write a short version in less than 30 minutes time.

Lee215 | Analysis | My understanding
I chose to study Lee215's analysis, and learn how to analyze the problem first.

Problem:
we can remove a stone if and only if,
there is another stone in the same column OR row.
We try to remove as many as stones as possible.

One sentence to solve:
Connected stones can be reduced to 1 stone,
the maximum stones can be removed = stones number - islands number.
so just count the number of "islands".

  1. Connected stones
    Two stones are connected if they are in the same row or same col.
    Connected stones will build a connected graph.
    It's obvious that in one connected graph,
    we can't remove all stones.

We have to have one stone left.
An intuition is that, in the best strategy, we can remove until 1 stone.

I guess you may reach this step when solving the problem.
But the important question is, how?

Lee215 | Union find algorithm | My understanding
I chose to study Lee215 analysis first.

  1. Union-Find
    I use union find to solve this problem.
    As I mentioned, the elements are not the points, but the indexes.

for each point, union two indexes.
return points number - union number
Copy a template of union-find,
write 2 lines above,
you can solve this problem in several minutes.

Complexity
union and find functions have worst case O(N), amortize O(1)
The whole union-find solution with path compression,
has O(N) Time, O(N) Space

Case study | Example 1 | All are connected
Input: stones = [[0,0],[0,1],[1,0],[1,2],[2,1],[2,2]]
Output: 5

image

Given stones in the array [[0,0],[0,1],[1,0],[1,2],[2,1],[2,2]], and I mark the matrix 3 x 3 using A, B, C, D, E, F, G, those six elements are marked as A, B, D, F, H, I. A and B are connected in the same row, A and D are connected in the same column, D and F are connected in the same row, F and I are connected in the same column, H and I are connected in the same row, so A, B, D, F, H and I are all connected in one disjoint set. D, B, F, H, I can be removed, and only one node needs to stay as parent node in one disjoint set.

Union find algorithm - 15 minutes warmup
I always like to refresh my favorite lecture notes about union find algorithm lecture note.
https://algs4.cs.princeton.edu/15uf/

Every time I like to learn something new from the lecture notes. This time I like to quickly review weighted quick-union idea. It is fun to review algorithm in detail before I read C# implemented solutions.

Here are 7 various ideas to write DFS/ Union find algorithms. Read one or two if you have time. Also I like to review those top player's code and give some advice how to make improvements.

  1. DFS algorithm - Build an undirected graph, and then search using DFS - mark visited to avoid deadloop
  2. DFS algorithm - Similar to Item 2 DFS algorithm. Two players chose DFS, almost same. Solution 1 is to define undirected graph using List[1123], solution 2 is to define the graph using bool[1005][1005]. Only concern is sparse arrray, it is more space efficient to use List[1123], but List<IList> is best choice.
  3. Union find algorithm - with Rank option, I like to learn how to define Rank, first design concern is when to increment one, second one is to assign high rank to be parent node when two disjoint sets union together. In general, to write a working union find algorithm, there are at least three things to work on: path compression, quick union and also which one to be a root based on rank or size or other things.
  4. Union find algorithm - Only getRoot API with path compression
  5. Union find algorithm - getFather API with path compression
  6. Union find algorithm - union API, getParent API, quick-union and path compression
  7. Union algorithm - find API, union API, Size of disjoint set

It is one place to review all features of union find - how to define quick union, rank, size, and also path compression. I find that this is best and quick way to get familiar with union find algorithm and easy to review for a code assessment or learn an advanced graph algorithm.

I think that it is important to review DFS solution first, since it takes less time to write and also easy to follow.

Solution 1: DFS

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

namespace _947_remove_stones_rank_16
{
    class Program
    {
        /// <summary>
        /// Leetcode 947 remove stones
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            RunTestcase2(); 
        }

        public static void RunTestcase1()
        {
            var stones = new int[6][]{
                new int[]{0,0},
                new int[]{0,1},
                new int[]{1,0},
                new int[]{1,2},
                new int[]{2,1},
                new int[]{2,2}
            };

            var result = RemoveStones(stones);
        }

        public static void RunTestcase2()
        {
            var stones = new int[5][]{
                new int[]{0,0},
                new int[]{0,2},
                new int[]{1,1},
                new int[]{2,0},
                new int[]{2,2}                
            };

            var result = RemoveStones(stones);
        }

        public static List<int>[] graph = new List<int>[1123];
        public static int[] Visited = new int[1123];

        /// <summary>
        /// study code
        /// https://leetcode.com/contest/weekly-contest-112/ranking/
        /// Ranking No. 16 sohelH
        /// Two tips to construct a undirected graph
        /// 1. brute force all possible edges
        /// 2. check if two nodes are in the same row or same column. If it is same row or column, 
        /// then the two nodes are connected. 
        /// </summary>
        /// <param name="stones"></param>
        /// <returns></returns>
        public static int RemoveStones(int[][] stones)
        {
            int length = stones.Length;

            for (int i = 0; i < length; i++)
            {
                graph[i] = new List<int>();
                Visited[i] = 0;
            }

            for (int i = 0; i < length; i++)
            {
                for (int j = i + 1; j < length; j++)
                {
                    int r1 = stones[i][0];
                    int c1 = stones[i][1];
                    int r2 = stones[j][0];
                    int c2 = stones[j][1];

                    if (r1 == r2 || c1 == c2)
                    {
                        graph[i].Add(j);
                        graph[j].Add(i);
                    }
                }
            }

            int components = 0;
            for (int i = 0; i < length; i++)
            {
                if (Visited[i] == 0)
                {
                    dfs(i);
                    components++;
                }
            }

            return length - components;
        }

        private static void dfs(int u)
        {
            if (Visited[u] == 1)
            {
                return;
            }

            Visited[u] = 1;
            for (int i = 0; i < graph[u].Count; i++)
            {
                dfs(graph[u][i]);
            }
        }
    }
}

Here is another DFS solution. I like to read C# code and think about how to improve the code.

Solution 2: DFS | Second solution of DFS

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

namespace _947_remove_stones___DFS
{
    class Program
    {
        /// <summary>
        /// Leetcode 947 remove stones
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            RunTestcase2(); 
        }

        public static void RunTestcase1()
        {
            var stones = new int[6][]{
                new int[]{0,0},
                new int[]{0,1},
                new int[]{1,0},
                new int[]{1,2},
                new int[]{2,1},
                new int[]{2,2}
            };

            var result = RemoveStones(stones);
        }

        public static void RunTestcase2()
        {
            var stones = new int[5][]{
                new int[]{0,0},
                new int[]{0,2},
                new int[]{1,1},
                new int[]{2,0},
                new int[]{2,2}                
            };

            var result = RemoveStones(stones);
        }

        public static int Rows; 
        public static bool[][] graph = new bool[1005][];
        public static bool[]   visited = new bool[1005];

        /// <summary>
        /// study code
        /// https://leetcode.com/contest/weekly-contest-112/ranking/
        /// Ranking No. 5 joon_young
        /// </summary>
        /// <param name="stones"></param>
        /// <returns></returns>
        public static int RemoveStones(int[][] stones)
        {
            Rows = stones.Length;

            for (int row = 0; row < 1005; row++)
            {
                graph[row] = new bool[1005];
            }

            // reset to false
            for (int row = 0; row < 1005; row++)
            {
                visited[row] = false; 
            }

            for (int row = 0; row < Rows; row++)
            {
                for (int j = row + 1; j < Rows; j++)
                {
                    if (stones[row][0] == stones[j][0] ||
                        stones[row][1] == stones[j][1])
                    {
                        graph[row][j] = true;
                        graph[j][row] = true;
                    }
                }
            }

            int result = 0;
            for (int row = 0; row < Rows; row++)
            {
                if(visited[row])
                {
                    continue;
                }

                result++;

                dfs(row);
            }

            return Rows - result; 
        }

        private static void dfs(int id)
        {
            if (visited[id])
                return;

            visited[id] = true; 

            for (int row = 0; row < Rows; row++)
            {
                if (graph[id][row])
                {
                    dfs(row);
                }
            }
        }
    }
}

Solution 3: Union find - Rank
The following C# code passes online judge.

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

namespace _947_remove_stones___union_join
{
    class Program
    {
        static void Main(string[] args)
        {
            var stones = new int[6][]{
                new int[]{0,0},
                new int[]{0,1},
                new int[]{1,0},
                new int[]{1,2},
                new int[]{2,1},
                new int[]{2,2}
            };

            var result = RemoveStones(stones);
        }

        public static int[] Parent;
        public static int[] Size;
        public static int[] Rank;

        public static int   Total; 

        /// <summary>
        /// https://leetcode.com/contest/weekly-contest-112/ranking
        /// rank No. 13 yangmei555
        /// 
        /// </summary>
        /// <param name="stones"></param>
        /// <returns></returns>
        public static int RemoveStones(int[][] stones)
        {
            var length = stones.Length;

            Parent = new int[length];
            Size = new int[length];
            Rank = new int[length];

            for (int i = 0; i < length; i++)
            {
                Parent[i] = i;
                Size[i] = 1; 
            }

            Total = length;

            for (int i = 0; i < length; i++)
            {
                for (int j = i + 1; j < length; j++)
                {
                    if (stones[i][0] == stones[j][0] ||
                       stones[i][1] == stones[j][1])
                    {
                        union(i, j);            
                    }
                }
            }

            return length - Total; 
        }

        /// <summary>
        /// code review:
        /// union and rank is simplified, and this version of code is easy to follow
        /// Rule 1: Parent node has higher rank compared to its child node
        /// union is to connect two root nodes of tree. 
        /// </summary>
        /// <param name="i"></param>
        /// <param name="j"></param>
        private static void union(int i, int j)
        {
            int id1 = find(i);
            int id2 = find(j);

            if (id1 != id2)
            {
                Total--;

                if (Rank[id1] <= Rank[id2])
                {
                    Parent[id1] = id2;
                    if (Rank[id1] == Rank[id2])
                    {
                        Rank[id2]++;
                    }
                }
                else
                {
                    Parent[id2] = id1; 
                }
            }
        }

        /// <summary>
        /// code review:
        /// find is to search tree structure and find the root of tree the node stays
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        private static int find(int id)
        {
            if (id != Parent[id])
            {
                Parent[id] = find(Parent[id]);
            }

            return Parent[id];
        }
    }
}

4th solution: Union find algorithm

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

namespace _947_ranking_117
{
    class Program
    {        
        public static void RunTestcase2()
        {
            var stones = new int[7][]{
                new int[]{3, 3},
                new int[]{4, 4},
                new int[]{1, 4},
                new int[]{1, 5},
                new int[]{2, 3},
                new int[]{4, 3},
                new int[]{2, 4}
            };

            var number = RemoveStones(stones);
        }

        static void Main(string[] args)
        {
            RunTestcase2(); 
        }

        /// <summary>
        /// https://leetcode.com/contest/weekly-contest-112/ranking/5/
        /// rank 117
        /// yajingleo
        /// 
        /// </summary>
        /// <param name="stones"></param>
        /// <returns></returns>
        public static int RemoveStones(int[][] stones)
        {
            var length = stones.Length;
            var root = new int[length];
            for (int i = 0; i < length; i++)
            {
                root[i] = i;
            }

            for (int i = 0; i < length; i++)
            {
                for (int j = 0; j < length; j++)
                {
                    if (stones[i][0] == stones[j][0] || stones[i][1] == stones[j][1])
                    {
                        var root1 = getRoot(root, i);
                        var root2 = getRoot(root, j);

                        // given constraint on the root, smaller index will be the root. 
                        root[root2] = root1;
                    }
                }
            }

            int result = length;
            for(int i = 0; i < length; i++)
            {
                if(root[i] == i)
                {
                    result--; 
                }
            }

            return result; 
        }

        /// <summary>
        /// code review April 4, 2019
        /// It is better to use while loop instead of recursive function
        /// </summary>
        /// <param name="root"></param>
        /// <param name="i"></param>
        /// <returns></returns>
        private static int getRoot(int[] root, int i)
        {
            while (root[i] != i)
            {
                i = root[i];
            }

            return i; 
        }
    }
}

Solution 5: Union find algorithm

It is interesting to read the code related to how to determine which one should be the parent when two nodes join the same disjoint set. The node with higher index is selected to be the root of disjoint set.

 for (int i = 0; i < rows; i++)
 {
        for (int j = i + 1; j < rows; j++)
        {
               if (stones[i][0] == stones[j][0] || stones[i][1] == stones[j][1])
               {
                    int fi = getFather(i);
                    int fj = getFather(j);
                    father[fi] = fj;
               }
        }
 }       

Pay attention to the above double for loop, second loop variable j > i, and then j's parent will be i's parent's parent. Interesting? How to avoid writing code related to rank, it is quick and easy way to make it consistent this way.

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

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

            var stones = new int[6][]{
                new int[]{0,0},
                new int[]{0,1},
                new int[]{1,0},
                new int[]{1,2},
                new int[]{2,1},
                new int[]{2,2}
            };

            var result = RemoveStones(stones);
        }

        private static List<int> father;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="i"></param>
        /// <returns></returns>
        private static int getFather(int search)        
        {
            if (search == father[search])
            {
                return search;
            }

            int root = getFather(father[search]);

            father[search] = root;

            return root;                            
        }

        /// <summary>
        /// Leetcode 947
        /// https://leetcode.com/contest/weekly-contest-112/ranking/
        /// ranking 8
        /// Windsfantasy6
        /// </summary>
        /// <param name="stones"></param>
        /// <returns></returns>
        public static int RemoveStones(int[][] stones)
        {
            var rows = stones.Length;

            father = new List<int>();
            for (int i = 0; i < rows; i++)
            {
                father.Add(i);
            }

            for (int i = 0; i < rows; i++)
            {
                for (int j = i + 1; j < rows; j++)
                {
                    if (stones[i][0] == stones[j][0] || stones[i][1] == stones[j][1])
                    {
                        int fi = getFather(i);
                        int fj = getFather(j);
                        father[fi] = fj;
                    }
                }
            }        

            // Disjoint set count upper limit -> total number of rows or columns
            int result = 0;

            for (int i = 0; i < rows; i++)
            {
                int fi = getFather(i);
                if (i == fi)
                {
                    result++;
                }
            }

            return rows - result; 
        }
    }
}

Solution 6: Union find algorithm

I learned the lesson, and getParent API should be called instead of calling parent[i].

It is better to go over lecture notes to review union find algorithm first.
https://algs4.cs.princeton.edu/15uf/
https://algs4.cs.princeton.edu/15uf/QuickUnionPathCompressionUF.java.html

It is important to understand path compression, the binary tree should be flated and it is more efficient to do that. Also I need to learn again quick-union and path compression, getParent API has path-compression, and union API is a quick-union time complexity O(1) operation.

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

namespace _947_remove_stones_Rank_176
{
    class Program
    {
        static void Main(string[] args)
        {
            RunTestcase1();
        }

        public static void RunTestcase1()
        {
            var stones = new int[7][]{
                new int[]{3, 2},
                new int[]{0, 0},
                new int[]{3, 3},
                new int[]{2, 1},
                new int[]{2, 3},
                new int[]{2, 2},
                new int[]{0, 2}
            };

            var number = RemoveStones(stones); 
        }

        // [[3,3],[4,4],[1,4],[1,5],[2,3],[4,3],[2,4]]
        public static void RunTestcase2()
        {
            var stones = new int[7][]{
                new int[]{3, 3},
                new int[]{4, 4},
                new int[]{1, 4},
                new int[]{1, 5},
                new int[]{2, 3},
                new int[]{4, 3},
                new int[]{2, 4}
            };

            var number = RemoveStones(stones);
        }

        /// <summary>
        /// study code weekly contest 112
        /// Rank 176 
        /// https://leetcode.com/contest/weekly-contest-112/ranking/8/
        /// </summary>
        /// <param name="stones"></param>
        /// <returns></returns>
        public static int RemoveStones(int[][] stones)
        {
            var length = stones.Length;

            for (int i = 0; i < length; i++)
            {
                Parent[i] = i;              
            }           

            for (int i = 0; i < length; i++)
            {
                for (int j = i + 1; j < length; j++)
                {
                    if (stones[i][0] == stones[j][0] || stones[i][1] == stones[j][1])
                    {
                        if (Parent[i] != Parent[j])
                        {
                            union(i, j);
                        }
                    }
                }
            }
            
            int number = 0;
            for (int i = 0; i < length; i++)
            {
                if(i == Parent[i])
                {
                    number++;
                }
            }

            return length - number; 
        }

        public static int[] Parent = new int[1005];
        public static int[] Rank   = new int[1005];

        /// <summary>
        /// path compression
        /// https://algs4.cs.princeton.edu/15uf/QuickUnionPathCompressionUF.java.html
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        private static int getParent_Obsolete(int x)
        {           
            while (Parent[x] != x)
            {
                x = Parent[x];
            }
          
            return x; 
        }

        /// <summary>
        /// study code from 
        /// https://algs4.cs.princeton.edu/15uf/QuickUnionPathCompressionUF.java.html
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        public static int getParent(int p)
        {
            int root = p;
            while (root != Parent[root])
            {
                root = Parent[root];
            }

            while (p != root)
            {
                int newp = Parent[p];
                Parent[p] = root;
                p = newp;
            }

            return root;
        }

        /// <summary>
        /// this function has a bug 
        /// var root1 = Parent[id1] -- should be -> var root1 = getParent(id1)
        /// In other words, I need to separate intermediate node from root node of tree.
        /// </summary>
        /// <param name="id1"></param>
        /// <param name="id2"></param>
        private static void union_Buggy(int id1, int id2)
        {
            var root1 = Parent[id1];
            var root2 = Parent[id2];            

            Parent[root2] = root1;            
        }

        private static void union(int id1, int id2)
        {
            var root1 = getParent(id1);
            var root2 = getParent(id2);

            Parent[root2] = root1;
        }
    }
}

Solution 7: Union find algorithm

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

namespace _947_remove_stones___union_join
{
    class Program
    {
        static void Main(string[] args)
        {
            var stones = new int[6][]{
                new int[]{0,0},
                new int[]{0,1},
                new int[]{1,0},
                new int[]{1,2},
                new int[]{2,1},
                new int[]{2,2}
            };

            var result = RemoveStones(stones);
        }
        
        public static int[] Size   = new int[1005];
        public static int[] Parent = new int[1005];

        /// <summary>
        /// https://leetcode.com/contest/weekly-contest-112/ranking
        /// rank No. 40 biltharesatyendra
        /// 
        /// </summary>
        /// <param name="stones"></param>
        /// <returns></returns>
        public static int RemoveStones(int[][] stones)
        {
            var length = stones.Length;

            for (int i = 0; i < length; i++)
            {
                Parent[i] = i;
                Size[i] = 1;
            }          

            for (int i = 0; i < length; i++)
            {
                for (int j = i + 1; j < length; j++)
                {
                    if (stones[i][0] == stones[j][0] ||
                        stones[i][1] == stones[j][1])
                    {
                        union(i, j);
                    }
                }
            }

            // count of disjoint sets
            int answer = 0;
            for (int i = 0; i < length; i++)
            {
                if (find(i) == i)
                {
                    answer += Size[i] - 1; 
                }
            }

            return answer;
        }

        /// <summary>
        /// code review:
        /// union and rank is simplified, and this version of code is easy to follow
        /// Rule 1: Parent node has higher rank compared to its child node
        /// union is to connect two root nodes of tree, not input arguments id1 and id2.  
        /// </summary>
        /// <param name="i"></param>
        /// <param name="j"></param>
        private static void union(int id1, int id2)
        {
            id1 = find(id1); // To avoid the bug to confuse id1 and find(id1), change id1's value
            id2 = find(id2);

            if (id1 == id2)
            {
                return;
            }

            if (Size[id1] < Size[id2])
            {
                swap(ref id1, ref id2);
            };            
            
            Parent[id2] = id1;
            Size[id1] += Size[id2];           
        }

        public static void swap(ref int x, ref int y)
        {
            var tmp = x;
            x = y;
            y = tmp; 
        }

        /// <summary>
        /// code review:
        /// find is to search tree structure and find the root of tree the node stays
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        private static int find(int id)
        {
            if (id != Parent[id])
            {
                Parent[id] = find(Parent[id]);
            }

            return Parent[id];
        }
    }
}

graphunion findquick learnerlecture notes

关于利率,你需要知道的那些事儿

 Here is the link.



Monday, June 20, 2022

Why Recessions May Be Inevitable

June 20, 2022 

Here is the link. 

America has experienced at least 30 recessions throughout history, dating back as early as 1857. Some experts believe that they have become an inevitable part of the economic cycle that fluctuates between periods of expansion and contraction. Nonetheless, certain measures can still be taken to make recessions less likely. As the nation’s authority on monetary policies, the Federal Reserve plays a critical role in managing recessions. So why do recessions happen and what can the Fed do about it? Watch the video to find out. The U.S. has experienced at least 30 recessions throughout history, dating back as early as 1857. Some economists argue that they may have become an inevitable part of the financial cycle that fluctuates between periods of expansion and contraction. “History teaches us that recessions are inevitable,” said David Wessel, a senior fellow in economic studies at The Brookings Institution. “I think there are things we can do with a policy that makes recessions less likely or when they occur, less severe. We’ve learned a lot, but we haven’t learned enough to say that we’re never going to have another recession.” As the nation’s authority on monetary policies, the Federal Reserve plays a critical role in managing recessions. The Fed is currently attempting to avoid a recession by engineering what’s known as a “soft landing,” in which incremental interest rate hikes are used to curb inflation without pushing the economy into recession. “What they’re trying to do is raise rates enough so demand slows,” said Jason Snipe, chief investment officer at Odyssey Capital Advisors. But a successful soft landing is extremely rare as the monetary policy needed to slow down the economy is often enforced too late to make any meaningful impact. It was arguably achieved just once, in 1994, thanks to the Fed’s more proactive response to inflation and good timing. ″[It’s] really, really difficult to get into that really, really narrow zone,” said Stephen Miran, former senior advisor at the U.S. Department of Treasury. “It’s the difference between trying to land an airplane in a really wide and spacious open field versus trying to land an airplane on a very, very narrow piece of land with rocks and water on either side.” Some experts also argue that policies have a limitation on what they can achieve against an impending downturn. “Policy tends to operate with long lags, which means the ability to effect immediate change in the economy is quite slow. I also think that increasingly we live in a global economy where the cross-currents that are impacting the economic dynamics are very complex,” said Lisa Shalett, chief investment officer, wealth management at Morgan Stanley. “These are dynamics that the Fed doesn’t have the tools to address and so to a certain extent, we do think that policymakers have certainly developed more tools to fight recessions,” she said. “But we don’t think that you can rely on policymakers to prevent recessions” » Subscribe to CNBC: https://cnb.cx/SubscribeCNBC » Subscribe to CNBC TV: https://cnb.cx/SubscribeCNBCtelevision About CNBC: From 'Wall Street' to 'Main Street' to award winning original documentaries and Reality TV series, CNBC has you covered. Experience special sneak peeks of your favorite shows, exclusive video and more.

LUV equity:

 

How Does Southwest Airlines Compare To Their Peers?

Southwest is part of the big four airline operators alongside legacy carriers such as American Airlines, Delta Air Lines, and United Airlines, and the rest of the market is fractioned among smaller players such as Spirit Airlines, Frontier, Alaska Air Group, Inc. (ALK), JetBlue Airways, and SkyWest, Inc. (SKYW). 

The legacy peers of Southwest have exhibited similar revenue rebound trends to that of Southwest in the last few quarters. The revenue of American Airlines grew 122% year-over-year to $8.9 billion in Q1 which is around 84% of 2019 levels. United Airlines reported stellar revenue growth of 134.9% YoY to $7.6 billion which is around 79% of pre-pandemic levels. Delta also reported a 159% YoY growth in revenue to $9.35 in Q1, which comes to around 89% of pre-pandemic levels. Southwest revenue, as highlighted earlier, recovered to 91% pre-pandemic levels in the first quarter, so it would be fair to conclude the company is performing in line or slightly better than its legacy peers from a revenue growth perspective.

If we look at the stock market performance of the legacy airlines group this year, Southwest is the only company to deliver positive total returns. We believe this comes down to the company's strong presence in domestic markets, comparatively better management of fuel costs, and strong liquidity position.