Amazon Interview Question for Software Engineer / Developers


Country: United States
Interview Type: Phone Interview




Comment hidden because of low score. Click to expand.
7
of 7 vote

we can create a max heap of songs where in the heap is arranged as per the count of the song.

Or
if we need only the song which is played max times then we can have a hash of songs where song can be the key and contains count as value. we also have a max variable which contains the max count of a song at that time. When we encounter a song we increase the count of the song by one and update the max if the count is more than max

- DashDash April 05, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
1
of 1 vote

Here is a possible solution:

For the data structures part: you could use an 'IndexedPriorityQueue<song, count>' (Hash map along with a heap for each song in the album).
Maintain a Trie (or one trie for each.. say, a server). The trie would have the album as the key and our IndexedPriorityQueue as value.

For maintaining the count, before increasing a count check if the count reaches the max of Double capacity. You may need to do some normalization of values. However, you might need to take care that the amortized worst case complexity would be still be O(number of songs in the album).

Since the number of songs is assumed to be small(<100) for each album. The reheapification process for heap won't affect the amortized run time complexity over the time.

Any constructive suggestions would be helpful. :-)

The class structure can be something like this,

public class MusicStore{

//SINCE THERE ARE HUGE NUMBER OF STRINGS. WE MAY USE A TRIE
 Map trie = new Trie();
 
 public void play(String album, String song){
  if(!trie.contains(album)){
   return;
  }
  
  /*
  You increase the priority of the song in the data structure.
  */
  IndexedPriorityQueue q = trie.get(album);
  double delta = 1;
  q.changePriority(song, delta);
 }
 
 public String topSong(String album){
 
 // PULL OUT THE ALBUM AND THEN THE SONG WITH MAX COUNT.
  IndexedPriorityQueue q = trie.get(album);
  return q.getMax();
 }
 
 //METHOD STUB FOR ADDING SONGS AND ALBUMS
 public addAlbumSong(String album, String song){
 }
}

/*############################*/

private class IndexedPriorityQueue{
//HEAP IS USED FOR MAINTAINING THE TOP SONG.
 private Queue heap;
// USED TO GET QUICK ACCESS TO THE SONG AND CHANGE THE COUNT.
 private Map map;
 
//THIS WOULD ALLOW LARGE NUMBER OF PLAY COUNTS TO BE STORED.
 private double scale;
 
 public IndexedPriorityQueue(){
  map = new Hashtable<String, Double>();
  
  /*COMPARATOR IS USED TO FACILITATE THE FORMATION OF HEAP USING THE COUNT VALUES FROM MAP*/
  Comparator<String> c = new Comparator<String>(){
   int compareTo(String o1, String o2){
    if(map.get(o1) < map.get(o2)){
     return -1;
    }
    if(map.get(o1) > map.get(o2)){
     return 1;
    }
    return 0;
   }
  };
  
  heap = new PriorityQueue<String>(1,c);
 }
 
 public void changePriority(Strig key, int delta){
  if(!hash.contains(key)){
  /*GET THE COUNT CHANGE IT BY DELTA, AND PUT IT IN THE HASH. you might have to do some data transformation for the heap count before adding a new count.*/
   hash.put(key, hash.get(key) + count);
   
   //REHEAPIFY THE HEAP
   heap.remove(key);
   heap.add(key);
  }else{
   hash.put(key, 1);
   heap.add(key);
  }
 }
 
 public String getMax(){
 //JUST GET THE TOP SONG.
  return heap.peek();
 }
}

- nik April 05, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 votes

Also,
Time: play() => ~O(log(num of songs in album) for each call. and for getMax() => ~O(1)
Space: O(num of songs + num of albums);
Some tweaks will improve the average case.

- nik April 05, 2013 | Flag
Comment hidden because of low score. Click to expand.
1
of 1 vote

Class Player
{
    public:
        void play(string bandName, string songName);
        string topSong(string bandName);
        
    private:
        Hash<string bandName, Class Band> bands = new Hash<string, Class Band>();
}

Class Band
{
    public:
        string Name;
        void play(string songName);
        string topSong();
        
     private:
        Hash songs = new <string songName, Song songs>();      
        // Will ensure that the topSong variable is updated by one thread at a time
        volatile song topSong;
}

Class Song
{
    public:
        Song song;
        void IncrementRequest();
        int GetNumberofRequests();
        
        private:
        int numberOfRequests;    // initialized to 0 in the constructor
}

string topSong()
{
    return this.topSong;
}

void play(string SongName)
{
    // Assuming that in case of same number of plays, the latest song wins
    if (topSpong.GetNumberOfRequests <= (song.GetNumberOfRequests + 1))
     {
        topSong = song;
     }
        
     song.incrementRequest();   
    
    song.play();
}

void IncrementRequest()
{
    this.numberOfRequests++;
}

int GetNumberOfRequests()
{
    return numberOfRequests;
}

- JSDUDE April 06, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 2 vote

I'd use a Hashtable with BandName as the key, and a sorted linkedlist of struct as the value. Every times a song is played, the linkedlist gets updated by removing the old Song and inserting a new song with song.times incremented.

Hashtable Band(string name, LinkedList<Songs> linkedList);
struct Songs
{
	int times;
	string title;
}

- iloveseahawks April 06, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

Hashtable within a Hashtable should do the job. The outer Hashtable has the albums and the inner Hashtable has the songs. The the outer Hashtable can also have a String and count for the top song and max play count along with the song map. This is depicted below using class albumDetails. These 2 fields keep getting updated each time a request to play comes in.

Class albumDetails ()
{
	String *topSong;
	int topSongCount;
	HashTable *hashSongs;
}

void playSongs (String album, String song)
{
	albumDetails *aAlbumDetail = (albumDetails*) hashTable [album];
	Hashtable *hashSongs = aAlbumDetails.hashSongs;
	int currentCount = hashSongs [song];
	currentCount++;
	hashSongs [song] = currentCount;
	if (currentCount >= aAlbumDetails.topSongCount)
	{
		aAlbumDetails.topSongCount = currentCount;
		aAlbumDetails.topSong = song;
	}
}

String topSong (album)
{
	albumDetails *aAlbumDetail = (albumDetails*) hashTable [album];
	return aAlbumDetails.topSong;
}


main ()
{
	// Loading data into memory.
	HashTable albums = new HashTable <String, albumDetails>;
	HashTable hashSongs = new HashTable <String, int>;
	hashSongs ["Sweet Child Of Mine"] = 0;
	hashSongs ["November Rain"] = 0;
	hashSongs ["Welcome To The Jungle"] = 0;
	albums ["Guns N Roses"] = new ablumDetails ("", 0, hashSongs);

	:	
	:
}

- KrisS April 17, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

I suggest a different approach - please tell me what I'm missing from the question.

Store is where the web services are directed at, it holds a hash table of the bands.
Getting a band takes an O(1) containing it takes O(#bands) space

public class Store {
	Hashtable<String, Band> bands;  //hash table - key is the band name
        
	public void play(String band, String song) {
            Band bReq = bands.get(band);
            bReq.playSong(song);
        }
  
        String topSong(String band) {
	    Band bReq = bands.get(band);
            return bReq.getTopSong();
        }
}

The Band class holds a sorted LinkedList in order to make seaching for a specific
song when requested by play faster - we can search for a song with binary search,
therefore it will only take O(log(#songs)) to find it and takes O(#songs) to contain.

The top song is always held be a separate reference so it can be retried and updated
within O(1) - updating takes O(1) because searching for it already took O(log(#songs))

public class Song {
	String name;
	String freq;
	String album; //not needed for this question

	public Song(String name) {
		this.name = name;
	 	this.freq    = 0;  //initial value
	}

	void playSong() {
		freq++;
		... //actions related to actual playing
	}
}

public class Band {
	LinkedList<Song> songs;
	Song                        topSong;

        void addSong(String name) { songs.add(new Song(name)); }
        void playSong(String name) { 
		Song s = songs.binarySearch(name);
		s.playSong();

		if(s.freq > topSong.freq)
			topSong = s;
	}

	String getTopSong() { return topSong.name; }
}

We can perhaps replace the LinkedList with something else, even another Hashtable<String, Song> where the key is the song name and the value is the Song class. In this case the size will not change but the search for the song can be O(1)

- YD September 10, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

We can have hash within a hash
Where Key is bandname and its value will be another hash which contains song as the key and value will be the number of times the song has been played.

{
Band1 => {
"Song1" => Count of number of times played
''Song2" => Count of number of times played

}
Band2 => {
"Song3" => Count of number of times played
''Song4" => Count of number of times played
}
}

- Anonymous November 06, 2017 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

I would use hash within a hash, where
Outerhash will have Bandname as the key and its value will be another hash containing songname as the key and number of times this song has been played as the value.

{
Band1 => {
song1 => count of times song1 played
song2 => count of times song2 played
}
Band2 => {
song3 => count of times song1 played
song4 => count of times song2 played
}
}


============================================================================================================================
CODE:

import java.util.HashMap;
import java.util.Map;

public class SongAlbum {

    Map<String, Map<String, Integer>> band_song_count = new HashMap <String, Map<String, Integer>>();

    public void play(String bandname, String songname){
        System.out.println("BandName :" + bandname + " --->Song :" + songname);
        //Increment the count for song played in the band_song_count hashmap

        //first check if contains bandname key
        Map tempMap = band_song_count.get(bandname);
        if(band_song_count.containsKey(bandname))
        {
            if(tempMap.containsKey(songname)) {
                int new_count = (int) tempMap.get(songname) + 1;
                tempMap.put(songname, new_count);
            }
            else
            {
                tempMap.put(songname, 1);
            }
        }
        else
        {
            band_song_count.put(bandname, tempMap = new HashMap<>());
            band_song_count.get(bandname).put(songname, 1);
        }
    }
    String topSong(String bandname) {
        //parsing logic
        String song_name = null;
        int max_count = 0;
        if(band_song_count.containsKey(bandname))
        {
            for(Map.Entry<String , Integer> t: band_song_count.get(bandname).entrySet())
            {
                if(t.getValue() > max_count)
                {
                    song_name = t.getKey();
                    max_count = t.getValue();
                }
            }
        }
        System.out.println("Maximum Song played is: " + song_name + " Played " + max_count + " times" );
        return song_name;
    }

    public void print(){
        for(Map.Entry <String, Map <String, Integer>> t :this.band_song_count.entrySet()){
            String bandIs = t.getKey();
            for (Map.Entry<String,Integer> e : t.getValue().entrySet())
                System.out.println("OuterKey:" + bandIs + " InnerKey: " + e.getKey()+ " VALUE:" +e.getValue());
        }
    }
}

- Neha Aggarwal November 06, 2017 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

I would use hash within a hash, where
Outerhash will have Bandname as the key and its value will be another hash containing songname as the key and number of times this song has been played as the value.

{
Band1 => {
song1 => count of times song1 played
song2 => count of times song2 played
}
Band2 => {
song3 => count of times song1 played
song4 => count of times song2 played
}
}


============================================================================================================================
CODE:

import java.util.HashMap;
import java.util.Map;

public class SongAlbum {

    Map<String, Map<String, Integer>> band_song_count = new HashMap <String, Map<String, Integer>>();

    public void play(String bandname, String songname){
        System.out.println("BandName :" + bandname + " --->Song :" + songname);
        //Increment the count for song played in the band_song_count hashmap

        //first check if contains bandname key
        Map tempMap = band_song_count.get(bandname);
        if(band_song_count.containsKey(bandname))
        {
            if(tempMap.containsKey(songname)) {
                int new_count = (int) tempMap.get(songname) + 1;
                tempMap.put(songname, new_count);
            }
            else
            {
                tempMap.put(songname, 1);
            }
        }
        else
        {
            band_song_count.put(bandname, tempMap = new HashMap<>());
            band_song_count.get(bandname).put(songname, 1);
        }
    }
    String topSong(String bandname) {
        //parsing logic
        String song_name = null;
        int max_count = 0;
        if(band_song_count.containsKey(bandname))
        {
            for(Map.Entry<String , Integer> t: band_song_count.get(bandname).entrySet())
            {
                if(t.getValue() > max_count)
                {
                    song_name = t.getKey();
                    max_count = t.getValue();
                }
            }
        }
        System.out.println("Maximum Song played is: " + song_name + " Played " + max_count + " times" );
        return song_name;
    }

    public void print(){
        for(Map.Entry <String, Map <String, Integer>> t :this.band_song_count.entrySet()){
            String bandIs = t.getKey();
            for (Map.Entry<String,Integer> e : t.getValue().entrySet())
                System.out.println("OuterKey:" + bandIs + " InnerKey: " + e.getKey()+ " VALUE:" +e.getValue());
        }
    }
}

- Neha Aggarwal November 06, 2017 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

I would use hash within a hash, where
Outerhash will have Bandname as the key and its value will be another hash containing songname as the key and number of times this song has been played as the value.

{
  Band1 => {
		song1 => count of times song1 played
                song2 => count of times song2 played
           }
  Band2 => {
		song3 => count of times song1 played
                song4 => count of times song2 played
           }
}

============================================================================================================================
CODE:

import java.util.HashMap;
import java.util.Map;

public class SongAlbum {

    Map<String, Map<String, Integer>> band_song_count = new HashMap <String, Map<String, Integer>>();

    public void play(String bandname, String songname){
        System.out.println("BandName :" + bandname + " --->Song :" + songname);
        //Increment the count for song played in the band_song_count hashmap

        //first check if contains bandname key
        Map tempMap = band_song_count.get(bandname);
        if(band_song_count.containsKey(bandname))
        {
            if(tempMap.containsKey(songname)) {
                int new_count = (int) tempMap.get(songname) + 1;
                tempMap.put(songname, new_count);
            }
            else
            {
                tempMap.put(songname, 1);
            }
        }
        else
        {
            band_song_count.put(bandname, tempMap = new HashMap<>());
            band_song_count.get(bandname).put(songname, 1);
        }
    }
    String topSong(String bandname) {
        //parsing logic
        String song_name = null;
        int max_count = 0;
        if(band_song_count.containsKey(bandname))
        {
            for(Map.Entry<String , Integer> t: band_song_count.get(bandname).entrySet())
            {
                if(t.getValue() > max_count)
                {
                    song_name = t.getKey();
                    max_count = t.getValue();
                }
            }
        }
        System.out.println("Maximum Song played is: " + song_name + " Played " + max_count + " times" );
        return song_name;
    }

    public void print(){
        for(Map.Entry <String, Map <String, Integer>> t :this.band_song_count.entrySet()){
            String bandIs = t.getKey();
            for (Map.Entry<String,Integer> e : t.getValue().entrySet())
                System.out.println("OuterKey:" + bandIs + " InnerKey: " + e.getKey()+ " VALUE:" +e.getValue());
        }
    }
}

- Neha Aggarwal November 06, 2017 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

I would use hash within a hash, where
Outerhash will have Bandname as the key and its value will be another hash containing songname as the key and number of times this song has been played as the value.

{
  Band1 => {
		song1 => count of times song1 played
                song2 => count of times song2 played
           }
  Band2 => {
		song3 => count of times song1 played
                song4 => count of times song2 played
           }
}

- Neha Aggarwal November 06, 2017 | Flag Reply
Comment hidden because of low score. Click to expand.
-1
of 1 vote

void play(String bandname, String songname);
- Use HashMap with key = "bandname + songname" & value = "music file path"

String topSong(String bandname);
- Use HashMap with key = "bandname" & value = "top song name"

- running April 05, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 votes

Very well, :-)
haha, No need to use a priority queue!
Sometimes we tend to miss the obvious. :-/

- nik April 05, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

I don't get how this simple solution works. If you don't keep track of how many times a song is requested, how can you tell it is a top song? The solution seems to return the most recent one. In addition, the ways keys are constructed in play(...) and topSong(...) are different. They are supposed to be the same, right? Thanks!

- Dr.Evil April 05, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

The solution given by '@running' DOES NOT WORK RIGHT AWAY. My bad, I should have pointed out that earlier.

using the value field for the count in ("bandname + songname" & value = count) can help.

The Hash would have to store the count of the songs. But you can simply keep a top song field for each album. When you increase the count. just compare it with the top one(using info. from the other hash and then pulling the count from the prev. hash). then change the top song accordingly.

for playing: use the album+song as key to increase the count. Then use the top song hash to cross check the play count with the recently increased song count. And then make the necessary changes.

However, scalability could be an issue since you are replicating the 'album' string multiple times. Also, the song name is stored twice. Space complexity is O(no. of albums * avg.no. of songs per album) + O(all albums). For time, play() => ~O(1). this may not be a big improvement considering the small size of the number of songs.

This solution is useful for finding the top song. But won't work when you are asked for the top three songs or so... for that you would require to use a Heap or an store which runs insertion sort on it. (Correct me if I am wrong, insertion sort would work the best for small sized nearly sorted arrays).

- nik April 05, 2013 | Flag


Add a Comment
Name:

Writing Code? Surround your code with {{{ and }}} to preserve whitespace.

Books

is a comprehensive book on getting a job at a top tech company, while focuses on dev interviews and does this for PMs.

Learn More

Videos

CareerCup's interview videos give you a real-life look at technical interviews. In these unscripted videos, watch how other candidates handle tough questions and how the interviewer thinks about their performance.

Learn More

Resume Review

Most engineers make critical mistakes on their resumes -- we can fix your resume with our custom resume review service. And, we use fellow engineers as our resume reviewers, so you can be sure that we "get" what you're saying.

Learn More

Mock Interviews

Our Mock Interviews will be conducted "in character" just like a real interview, and can focus on whatever topics you want. All our interviewers have worked for Microsoft, Google or Amazon, you know you'll get a true-to-life experience.

Learn More