DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • Arrays and Hashing
  • Floyd's Cycle Algorithm for Fraud Detection in Java Systems
  • Metal and the Simulated Annealing Algorithm
  • Understanding HyperLogLog for Estimating Cardinality

Trending

  • Prioritizing Cloud Security Risks: A Developer's Guide to Tackling Security Debt
  • Strategies for Securing E-Commerce Applications
  • Designing AI Multi-Agent Systems in Java
  • Yet Another GenAI Nightmare: Seven Shadow AI Pitfalls to Avoid
  1. DZone
  2. Data Engineering
  3. AI/ML
  4. Arrays and Hashing. Vol.2

Arrays and Hashing. Vol.2

Sergei Golitsyn provides solutions to popular problems experienced using arrays and hashing.

By 
Sergei Golitsyn user avatar
Sergei Golitsyn
·
Jan. 04, 23 · Tutorial
Likes (3)
Comment
Save
Tweet
Share
2.3K Views

Join the DZone community and get the full member experience.

Join For Free

Aloha guys. We are going on about popular interview questions. Today we will solve a few problems with hashing approach. 

Our first problem is:

Top K Frequent Elements

Top K Frequent Elements

Description

Given an integer array nums and an integer k, return the k most frequent elements. You may return the answer in any order.

Example 1:

Input: nums = [1,1,1,2,2,3], k = 2
Output: [1,2]


Example 2:

Input: nums = [1], k = 1
Output: [1]


Solution

So, we need to return K top elements. Based on this condition, we understand that we should calculate each element's frequency. But how can we do it? Sure let's create a hash map and calculate each element's frequency. The Key will be the element, and the value will be frequency. 

Ok, for now, we have elements frequency, but how can we get top K elements? For example, we can create our own max heap and get k elements. If you don't want to write your own heap, you can use Java PriorityQueue. When you create a priority queue, you should pass the comparator - we are going to sort by value. After it, we can poll K elements and add them to the result. Go under the hood:

Code

Java
 
public int[] topKFrequent(int[] nums, int k) {
        int[] rez = new int[k];
        Map<Integer, Integer> map= new HashMap<>();

        for(int n : nums){
            map.put(n, map.getOrDefault(n, 0) + 1);
        }

        PriorityQueue<Map.Entry<Integer, Integer>> pq = new PriorityQueue<>((a,b) -> a.getValue() - b.getValue());

        for(Map.Entry<Integer, Integer> entry : map.entrySet()){
                pq.add(entry);
                if (pq.size() > k){
                    pq.poll();
                }
        }
        int i = k;
        while(!pq.isEmpty()){
            rez[--i] = pq.poll().getKey();

        }
        return rez;
    }


Was it clear to you guys? I hope so. 

Our next problem:

Product of Array Except Self

Product of Array Except Self

Description

Given an integer array nums, return an array answer such that answer[I]  is equal to the product of all the elements of nums except nums[I]. The product of any prefix or suffix of nums is guaranteed 

to fit in a 32-bit integer. You must write an algorithm that runs in O(n) time and without using the division operation.

Example 1:

Input: nums = [1,2,3,4]
Output: [24,12,8,6]


Example 2:

Input: nums = [-1,1,0,-3,3]
Output: [0,0,9,0,0]


Solution

I like this problem. It changed my mind. I thought we could multiply all elements and then divide the result on the current, but unfortunately, we cannot use the division operation. 

Let's think. What will we have if we multiply elements step by step from left to right except for self?

For example [1,2,3,4]:

 
0. nums = [1, 2, 3, 4]
   left = [0, 0, 0, 0]
1. it is obvious that for the first element we set 1
   because we don't have items to the left and we do not include self. 
   left = [1, 0, 0, 0]
2. Then for the nex element we get prev element * prev left element
   i = 1.
   nums[i-1] * left[i-1]. left = [1, 1, 0, 0]
3. Repeat this action till the end.
4. i = 2.
   nums[i-1] * left[i-1] = 2 * 1 = 1  left = [1, 1, 2, 0]
5. i = 3
   nums[i-1] * left[i-1] = 3 * 2 = 1  left = [1, 1, 2, 6]


As a result, we have all left products in the array except for self. With the same algo, let's find all the right products.

 
0. nums = [1, 2, 3, 4]
   right = [0, 0, 0, 0]
1. it is obvious that for the last element we set 1
   because we don't have items to the right and we do not include self. 
   left = [0, 0, 0, 1]
2. Then for the nex element we get prev element * prev left element
   i = 1.
   nums[i-1] * left[i-1]. left = [0, 0, 4, 1]
3. Repeat this action till the end.
4. i = 2.
   nums[i-1] * left[i-1] = 4 * 3 = 12  left = [1, 12, 4, 1]
5. i = 3
   nums[i-1] * left[i-1] = 12 * 2 = 24  left = [24, 12, 4, 1]


For now, we have products left except self and right except self. We can multiply them and receive a product of the array except for self.

 
0. left =  [1, 1, 2, 6]
   right = [24, 12, 4, 1]
   rez =   [0, 0, 0, 0]
1. rez = [1* 24, 1 * 12, 2 * 4, 6 * 1]
2. rez = [24, 12, 8, 6]


Wow, wisdom, isn't it? 

Code

Java
 
    public int[] productExceptSelf(int[] nums) {
        int n = nums.length;
        int[] toRight = new int[n];
        int[] toLeft = new int[n];
        
        toRight[0] = 1;
        toLeft[n-1] = 1;
        for(int i = 1; i < n; i++){
            toRight[i] = nums[i-1] * toRight[i-1];
        }
        for(int i = n-2; i >=0; i--){
            toLeft[i] = nums[i+1] * toLeft[i+1];
        }
        for(int i = 0; i < n; i ++){
            toRight[i] *= toLeft[i];
        }
        return toRight;
    }


Hey. have you ever tried to solve Sudoku? Our next task will help you to do it.

Valid Sudoku

Valid Sudoku

Description

Determine if a 9 x 9 Sudoku board is valid. Only the filled cells need to be validated according to the following rules:

  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 
  4. 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.

Example 1:

 
Input: board = 
[["5","3",".",".","7",".",".",".","."]
,["6",".",".","1","9","5",".",".","."]
,[".","9","8",".",".",".",".","6","."]
,["8",".",".",".","6",".",".",".","3"]
,["4",".",".","8",".","3",".",".","1"]
,["7",".",".",".","2",".",".",".","6"]
,[".","6",".",".",".",".","2","8","."]
,[".",".",".","4","1","9",".",".","5"]
,[".",".",".",".","8",".",".","7","9"]]
Output: true


Example 2:

 
Input: board = 
[["8","3",".",".","7",".",".",".","."]
,["6",".",".","1","9","5",".",".","."]
,[".","9","8",".",".",".",".","6","."]
,["8",".",".",".","6",".",".",".","3"]
,["4",".",".","8",".","3",".",".","1"]
,["7",".",".",".","2",".",".",".","6"]
,[".","6",".",".",".",".","2","8","."]
,[".",".",".","4","1","9",".",".","5"]
,[".",".",".",".","8",".",".","7","9"]]
Output: false
Explanation: Same as Example 1, except with the 5 in the top left corner being modified to 8. Since there are two 8's in the top left 3x3 sub-box, it is invalid.


Solution

Based on the description, we see three main requirements for a valid Sudoku:

  • Each row must contain the digits 1-9 without repetition.
  • Each column must contain the digits 1-9 without repetition.

  • Each of the nine 3 x 3 sub-boxes of the grid must contain the digits 1-9 without repetition.

Let's try to solve this problem step by step. First of all, let's check the row of the Sudoku. We can create a Map or an array, or a Set from 1 to 10 and fill needed cells. If we see this element twice, we will return false — our Sudoku is invalid.

The same approach we will use for columns. Just check column values. 

But how can we handle squares? Looks like we have a matrix 9x9. We have nine elements in a row, nine elements in a column, and nine squares. The main problem here is to correctly determine square elements. We must find the start/end column and the start/end row. For the column, the best idea will be to get a square number (For example, 3), then multiply it by three elements in a row and then divide it by square elements count (9). 

int startRow =  (square * 3) % 9 = 3 * 3 % 9 = 1;

With the end row, all will be easier. We just need to add three lines =) 

Now, let's figure out how to calculate the start column. For square 0, it should be 0. For square 1, it also should be 0. But for square 3, it should be 3, right? Looks like we need to get a square number, divide it by 3 (because we have three columns in a square) and then multiply it by three. 

 
For square 0: 0 / 3 * 3 = 0;
For square 1: 1 / 3 * 3 = 0;
For square 2: 0 / 3 * 3 = 0;
For square 3: 3 / 3 * 3 = 1;
For square 6: 6 / 3 * 3 = 6;


Is it clear? 

For now, we have three base checks. And we can iterate from 1 to 9 and check 9 rows, 9 columns, and 9 squares. 

Code

Java
 
public boolean isValidSudoku(char[][] board) {
        for (int i = 0; i < board.length; i++) {
            boolean isValidLine = isValidRow(board, i);
            boolean isValidRow = isValidColumn(board, i);
            boolean isValidSquare = isValidSquare(board, i);

            if (!isValidLine || !isValidRow || !isValidSquare) {
                return false;
            }
        }
        return true;
    }

    private boolean isValidRow(char[][] board, int line) {
        int[] dict = new int[10];
        for (int i = 0; i < board[line].length; i++) {
            char cur = board[line][i];
            if (Character.isDigit(cur)) {
                int curInt = cur - '0';
                if (curInt < 1 || curInt > 9) {
                    return false;
                }
                if (dict[curInt] > 0) {
                    return false;
                }
                dict[curInt]++;
            }
        }
        return true;
    }

    private boolean isValidColumn(char[][] board, int row) {
        int[] dict = new int[10];
        for (int i = 0; i < board.length; i++) {
            char cur = board[i][row];
            if (Character.isDigit(cur)) {
                int curInt = cur - '0';
                if (curInt < 1 || curInt > 9) {
                    return false;
                }
                if (dict[curInt] > 0) {
                    return false;
                }
                dict[curInt]++;
            }
        }
        return true;
    }

    private boolean isValidSquare(char[][] board, int square) {
        int[] dict = new int[10];

        int startRow = (square * 3) % 9;
        int endRow = startRow + 3;

        int startCol = (square / 3) * 3;
        int endCol = startCol + 3;
        for (int i = startCol; i < endCol; i++) {
            for (int j = startRow; j < endRow; j++) {
                char cur = board[i][j];
                if (Character.isDigit(cur)) {
                    int curInt = cur - '0';
                    if (curInt < 1 || curInt > 9) {
                        return false;
                    }
                    if (dict[curInt] > 0) {
                        return false;
                    }
                    dict[curInt]++;
                }
            }
        }
        return true;
    }


I know, I know a lot of code. But for now, we know how to check Sudoku. 

Longest Consecutive Sequence

Longest Consecutive Sequence

Description

Given an unsorted array of integers nums, return the length of the longest consecutive elements sequence. You must write an algorithm that runs in O(n) time.

Example 1:

Input: nums = [100,4,200,1,3,2]
Output: 4
Explanation: The longest consecutive elements sequence is [1, 2, 3, 4]. Therefore its length is 4.


Example 2:

Input: nums = [0,3,7,2,5,8,4,6,0,1]
Output: 9


Solution

Do you remember our topic? It is Arrays and Hashing. Let's think about how we can use it. We should find consecutive elements. For example, 1, 2, 3, 4, or maybe 10, 11, 12.  

As a brute force solution, we can try to sort an array and check all subsequences. But it will take O(NlogN) time with standard Java sort algorithms. 

Based on the base example, we understand that if we have 1 in the array, we should check 2. If we have 2 in the array, we have subsequence size two and want to check 3. Do we have it in the array? Let's try to check it, but without sorting. In this case, we need to iterate over the array N*N times. But what if we simply put all array elements into HashSet and then check them there? 

 
1. Put array elements into set
2. Iterate over set
3. Check element and element + 1
4. Do it till the end


But looks like this algorithm also will be N*N. It looks so, but let's check a code, and then I will try to explain.

Code

Java
 
public int longestConsecutive(int[] nums) {
        int max = 0;

        Set<Integer> set = new HashSet<>();

        for (int num : nums) {
            set.add(num);
        }
        for (Integer i : set) {
            if (!set.contains(i - 1)) {
                int prev = i;
                int curMax = 1;
                while (set.contains(prev + 1)) {
                    prev++;
                    curMax++;
                }
                max = Math.max(max, curMax);
            }
        }
        return max;
    }

Although the time complexity appears to be quadratic due to the while loop nested within the for loop, closer inspection reveals it to be linear. Because the while loop is reached only when i marks the beginning of a sequence (i.e., i-1 is not present in the array), the while loop can only run for N iterations throughout the entire runtime of the algorithm. This means that despite looking like O(N*N) complexity, the nested loops actually run in O(N+N)=O(n) time. All other computations occur in constant time, so the overall runtime is linear.

Data structure Array data type Sorting algorithm Algorithm

Opinions expressed by DZone contributors are their own.

Related

  • Arrays and Hashing
  • Floyd's Cycle Algorithm for Fraud Detection in Java Systems
  • Metal and the Simulated Annealing Algorithm
  • Understanding HyperLogLog for Estimating Cardinality

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!