You assign to the array every time in the foreach, thus overwriting it every time. So you end up with only having the last thing assigned to it. If you move the print inside the foreach you'll see that they are all there.

To store those keys you need to add them to the array, not assign. I've corrected the typo $k to $K, and changed the aa with f to ff.

This prints the line: ff aa bb. Every time through the loop all keys found in the hash for a particular array element are added to @c, each as a separate element. So @c will contain all bottom-level keys across the whole data structure.

However, there is more that I would like to suggest.

Alwaysuse strict; and use warnings; This is not pedantry but it directly helps. I never write code without them. The typo would be caught here, for example.

Use descriptive variable names. Avoid single-letter variable names, unless in very short loops or where it is crystal clear what they are. It is just too easy to confuse them. (For example, a typo like this couldn't really happen.) Most importantly, the code is going to be far nicer to work with, what generally results in better code.

Please use good indentation and spacing. It helps a lot, in many ways.

A useful package for nested data structures is Data::Dumper, which can print the whole thing nicely formatted so we can see it. Try to add to the end of your code

use Data::Dumper;
print Dumper(\%K);

This is a core module, no need to install it. There are yet others that do the same or similar.

Here is another way to do what you ask.

my @lowest_keys = map { sort keys %{$K{$_}} } @a;

I call them lowest_keys to emphasize that these are the ones from the last hash in your data structure, the bottom of it. The map applies processing in the block { ... } to each element of @a in turn, returning a list with all these results. (If any result itself is a list, with more elements than one, each becomes a separate element. So this may create the output list with many more elements than input.) This list can then be assigned to an array, as above, or passed on to another function that expects a list as input.

The map is generally used to transform an array into another, by doing to each element what is in { ... } block. Its close cousin is grep, which is used to filter, by passing only the elements of the input list for which the condition in { ... } evaluates to true, thus forming the output list. For example, filter out undefined array elements: @good = grep { defined } @all

Email codedump link for Why the array could not hold the keys of hash in the following perl script?