Am I following a good approach? - sorting

Please find the link to the discussed problem.
Sorting | Amazon Interview Question
I followed this following approach by taking random arrays. Would want your suggestions on what other approaches can be followed to solve the problem:-
public class ThreeMachineInsertionSorting {
public static void main(String[] args) {
int[] ar1 = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,-10};
int[] ar2 = { 20, 19, 18, 17, 16, 15, 14, 23, 12, 11, 10 };
int[] ar3 = { 20, 19, 21, 122, 10, 9, 11, 12, 4, 13, 18, 17 };
performInsertionSort(ar1, ar2, ar3);
}
private static void performInsertionSort(int[] ar1, int[] ar2, int[] ar3) {
int[][] arrayOfArrays = { ar1, ar2, ar3 };
int [] unSortedArray= mergeArrays(ar1,ar2,ar3,arrayOfArrays);
int i,j,key;
for(i=1;i<unSortedArray.length;i++){
key= unSortedArray[i];
j=i-1;
while(j>=0 && key<unSortedArray[j]){
unSortedArray[j+1] = unSortedArray[j];
j--;
}
unSortedArray[j+1] = key;
}
System.out.println("Size of the unSorted array is :=" + unSortedArray.length);
shareLoad(unSortedArray, arrayOfArrays);
}
private static void shareLoad(int[] sortedArray, int[][] arrayOfArrays) {
int loadFactor = sortedArray.length/3;
int index=0;
while(index<sortedArray.length){
for(int [] ar : arrayOfArrays){
for(int i=0; i<ar.length;i++){
if(i<=loadFactor){
ar[i] = sortedArray[index];
index++;
}
}
}
}
for(int [] ar : arrayOfArrays){
System.out.println("******************************************array properties**************************************");
System.out.println("Size of array::" + ar.length);
for(int i=0; i<ar.length;i++){
System.out.print(ar[i]+" ");
}
System.out.println("\n******************************************************************");
}
}
private static int[] mergeArrays(int[] ar1, int[] ar2, int[] ar3,int[][] arrayOfArrays ) {
int[] colaboratedArray = new int[ar1.length + ar2.length + ar3.length];
System.out.println("Length of multi-dimensional array :-"
+ arrayOfArrays.length);
int i = 0;
while (i < colaboratedArray.length) {
for (int[] ar : arrayOfArrays) {
for (int j = 0; j < ar.length; j++) {
colaboratedArray[i++] = ar[j];
}
}
}
return colaboratedArray;
}
}

Here is a bit of a naive solution.
Sort M1, M2 and M3 using in-place sorting algorithm.
Combine M1 and M3 by finding out the point where all elements from M1 can be swapped out for smaller elements in M3.
To find this point take 1/9 of the numbers in M3 and move to the buffer in M1. Notice that 1/9 of the numbers is exactly the 10% capacity available. We start with the smallest bucket in M3 and compare it to the largest bucket in M1. Just by comparing the largest value from the M3 bucket and the smallest value from the M1 bucket we can determine if we can swap them out completely or not. If we can then move the bucket from M1 to M3 and bring in new bucket from M3. Do this until we have M1 and M3 partitioned.
Now we must sort M1 and M3 again. Now we must take the smallest elements from M2 and move them to M1 until we have M1 and M2 partitioned. After that is done we can sort M1 and M2. Notice that M1 is now finished.
Finish by partitioning M2 and M3 and then sort both and the problem is solved.
Notice that this solution requires us to sort all machines three times, and each data element is in worst case moved 3 times between machines.
This solution can be improved if we can find the median of two unsorted sets, then we can partition the sets according to this median and then transferring elements, and only sort in the end once we know that the final numbers are on each machine.
It can be further improved if we can find the Kth largest element of 3 unsorted sets efficiently without transferring data.

Related

how do i solve this algorithm question(about dp and recursion maybe)

Here is the code challenge:
A restaurant has 𝑚 tables, each table can contain 𝑎𝑖 people, 𝑛 teams come to eat, and each team has 𝑏𝑖 people.
You can choose to serve or turn away a certain group. If you do not serve the group, the cost is 𝑏𝑖𝑥. If you do serve this group, then one table might not seat everyone, so they may need to sit at separate tables,
and the cost will be (𝑘−1)𝑦, where 𝑘 is the number of groups the team separated into.
A table can only be used by one team. Please calculate the least cost.
Example:
m n x y
5 2 5 3
a[] = {4,5,1,1,1}
b[] = {7,3}
output:6
Explanation:
The second team sits at the first table, the first team sits at 2nd, 3rd, 4th tables, and the cost is (3-1)*3=6
I have tried a lot times. First of all, I think this is a dynamic programming question. But I couldn't conclude the state transfer function and the example is not an optimal substructure it seems. So I tried to solve it by recursion, and calculate all the possibilities, but I couldn't make it work.
There is no known general and efficient solution to this problem. Not even a pseudo-efficient solution like dynamic programming.
That can be seen by reducing a special case of the 3-partition problem to this one. Given 3*m numbers summing to m*T with each number of size between T/4 and T/2, we set x=1, y=4, make the numbers into our tables, and have m groups of size T to seat. If there is a 3-partition of the set, the optimal solution will be to seat every group at 3 tables. (Why? Because no 2 tables are big enough to seat a group. So a 3-partition seats all groups optimally, and any seating of that cost is a 3-partition.) Therefore a solver for your problem is able to solve this special case of the 3-partition problem.
But this special case is strongly NP-complete. Which means that not only is it NP complete, but it doesn't even have a pseudo-polynomial solution that dynamic programming would give us.
I would personally solve this problem in practice by viewing it as a A* search. With a cost that I'd use a pair of numbers for, (cost, -groups_left). Even with a bad heuristic of the cost being 0 you can solve this greedily if a greedy solution is optimal. And with some work on a smart heuristic, this likely will perform well in practice.
That said, there will be pathological cases where it takes exponential time and memory to finish.
i try to solve it as follows :
make all permutations of tables
calculate all combination with mixing team & table order sequence.
sort by cost by ascending.
hope it can help you.
import java.util.* ;
public class TableAllotment {
static class Table {
int idx, numOfSeat ;
Table(int idx, int numOfSeat) {
this.idx = idx ;
this.numOfSeat = numOfSeat ;
}
public String toString() {
return String.format("t(%d:%d)", idx+1, numOfSeat) ;
}
}
static class Team implements Cloneable, Comparable<Team> {
int idx, numOfPerson ;
ArrayList<Table> tables ;
Team(int idx, int numOfPerson) {
this.idx = idx ;
this.numOfPerson = numOfPerson ;
tables = new ArrayList<>() ;
}
#Override protected Team clone() {
return new Team(idx, numOfPerson) ;
}
#Override public int compareTo(Team team) {
return toString().compareTo(team.toString()) ;
}
public String toString() {
return String.format("g(%d:%d),%s", idx+1, numOfPerson, tables) ;
}
}
static class Allotment implements Comparable<Allotment> {
TreeSet<Team> teams ;
int score ;
Allotment() {
teams = new TreeSet<>() ;
}
#Override public int compareTo(Allotment team) {
return toString().compareTo(team.toString()) ;
}
public String toString() {
return String.format("%6d, %s", score, teams) ;
}
}
TableAllotment(Integer[] tables, Integer[] teams, int x, int y) {
var tableList = new ArrayList<Table>() ; // initialize Table list with num of seat
for (int i = 0 ; i < tables.length ; i++) {
tableList.add(new Table(i, tables[i])) ;
}
var tableAnyOrderSeq = new ArrayList<ArrayList<Table>>() ; // make random table sequence
genAllOrderSeq(tableList, 0, tableAnyOrderSeq) ;
var teamList = new ArrayList<Team>() ; // initialize Team list with num of person
for (int i = 0 ; i < teams.length ; i++) {
teamList.add(new Team(i, teams[i])) ;
}
var results = new TreeSet<Allotment>() ;
calcByOrder(tableAnyOrderSeq, teamList, x, y, results) ;
System.out.println("Team arrival order by input :") ;
for (Allotment allocment : results) System.out.println(allocment) ;
// var teamAnyOrderSeq = new ArrayList<ArrayList<Team>>() ; // make random Team order sequence
// genAllOrderSeq(teamList, 0, teamAnyOrderSeq) ;
// results = new TreeSet<Allotment>() ;
// calcByRandomOrder(tableAnyOrderSeq, teamAnyOrderSeq, x, y, results) ;
// System.out.println("Team arrival order by random :") ;
// for (Allotment allocment : results) System.out.println(allocment) ;
}
static void calcByOrder(List<ArrayList<Table>> tablesAnyOrder, ArrayList<Team> teams,
int x, int y, TreeSet<Allotment> results) {
for (int i = 0 ; i < tablesAnyOrder.size(); i++) {
var tables = tablesAnyOrder.get(i) ;
var remainPerson = -1 ;
var teamIdx = -1 ;
var result = new Allotment() ;
Team team = new Team(-1,-1) ; // dummy initialize
for (int j = 0 ; j < tables.size(); j++) {
if (remainPerson <= 0) { // new Team
if (++teamIdx >= teams.size()) break ;
team = teams.get(teamIdx).clone() ;
remainPerson = team.numOfPerson ;
}
remainPerson -= tables.get(j).numOfSeat ;
team.tables.add(tables.get(j)) ;
if (remainPerson <= 0) {
Collections.sort(team.tables, (t1, t2) -> t1.idx==t2.idx ? 0 : t1.idx>t2.idx ? 1 : -1) ;
result.teams.add(team) ;
}
}
if (result.teams.size() > 0) {
for (Team team_ : result.teams) { // calc served score
result.score += (team_.tables.size() - 1) * y ;
}
for (int k = 0 ; k < teams.size() ; k++) { // find not served teams
var isServed = false ;
for (Team team__ : result.teams) {
if (teams.get(k).idx == team__.idx) {
isServed = true ;
break ;
}
}
if (!isServed) result.score += teams.get(k).numOfPerson * x ; // calc not served score
}
results.add(result) ;
}
}
}
// static void calcByRandomOrder(List<ArrayList<Table>> tablesAnyOrder, List<ArrayList<Team>> teamAnyOrder,
// int x, int y, TreeSet<Allotment> results) {
// for (int i = 0 ; i < teamAnyOrder.size(); i++) {
// var teams = teamAnyOrder.get(i) ;
// calcByOrder(tablesAnyOrder, teams, x, y, results) ;
// }
// }
static <T> void genAllOrderSeq(List<T> array, int k, List<ArrayList<T>> results) { // generate all order sequence
for (int i = k; i < array.size(); i++) {
Collections.swap(array, i, k) ;
genAllOrderSeq(array, k+1, results) ;
Collections.swap(array, k, i) ;
}
if (k == array.size() -1) results.add(new ArrayList<T>(array)) ;
}
public static void main(String[] args) {
new TableAllotment(new Integer[]{4,5,1,1,1}, new Integer[]{7, 3}, 5, 3) ;
}
}
Result :
Team arrival order by input :
6, [g(1:7),[t(2:5), t(3:1), t(4:1)], g(2:3),[t(1:4)]]
6, [g(1:7),[t(2:5), t(3:1), t(5:1)], g(2:3),[t(1:4)]]
6, [g(1:7),[t(2:5), t(4:1), t(5:1)], g(2:3),[t(1:4)]]
9, [g(1:7),[t(1:4), t(2:5)], g(2:3),[t(3:1), t(4:1), t(5:1)]]
9, [g(1:7),[t(1:4), t(3:1), t(4:1), t(5:1)], g(2:3),[t(2:5)]]
9, [g(1:7),[t(2:5), t(3:1), t(4:1), t(5:1)], g(2:3),[t(1:4)]]
9, [g(1:7),[t(2:5), t(3:1), t(4:1)], g(2:3),[t(1:4), t(5:1)]]
9, [g(1:7),[t(2:5), t(3:1), t(5:1)], g(2:3),[t(1:4), t(4:1)]]
9, [g(1:7),[t(2:5), t(4:1), t(5:1)], g(2:3),[t(1:4), t(3:1)]]
21, [g(1:7),[t(1:4), t(2:5), t(3:1)]]
21, [g(1:7),[t(1:4), t(2:5), t(4:1)]]
21, [g(1:7),[t(1:4), t(2:5), t(5:1)]]
24, [g(1:7),[t(1:4), t(2:5), t(3:1), t(4:1)]]
24, [g(1:7),[t(1:4), t(2:5), t(3:1), t(5:1)]]
24, [g(1:7),[t(1:4), t(2:5), t(4:1), t(5:1)]]
results are sorted by cost.
first group can sit at 2nd, 3rd, 4th tables.
first group can sit at 2nd, 3rd, 5th tables.
first group can sit at 2nd, 4th, 5th tables.
second group sits at the first table.
the cost are 6 also.
playground : https://www.sololearn.com/compiler-playground/cGSZq2NIbRxm

Cannot understand hoow to recursively merge sort

Currently self-learning C++ with Daniel Liang's Introduction to C++.
On the topic of the merge sort, I cannot seem to understand how his code is recursively calling itself.
I understand the general concept of the merge sort, but I am having trouble understanding this code specifically.
In this example, we first pass the list 1, 7, 3, 4, 9, 3, 3, 1, 2, and its size (9) to the mergeSort function.
From there, we divide the list into two until the array size reaches 1. In this case, we would get: 1,7,3,4 -> 1,7 -> 1. We then move onto the merge sorting the second half. The second half array would be 7 in this case. We merge the two arrays [1] and [7] and proceed to delete the two arrays that were dynamically allocated to prevent any memory leak.
The part I don't understand is how does this code run from here? After delete[] firstHalf and delete[] secondHalf. From my understanding, shouldn't there be another mergeSort function call in order to merge sort the new firstHalf and secondHalf?
#include <iostream>
using namespace std;
// Function prototype
void arraycopy(int source[], int sourceStartIndex,
int target[], int targetStartIndex, int length);
void merge(int list1[], int list1Size,
int list2[], int list2Size, int temp[]);
// The function for sorting the numbers
void mergeSort(int list[], int arraySize)
{
if (arraySize > 1)
{
// Merge sort the first half
int* firstHalf = new int[arraySize / 2];
arraycopy(list, 0, firstHalf, 0, arraySize / 2);
mergeSort(firstHalf, arraySize / 2);
// Merge sort the second half
int secondHalfLength = arraySize - arraySize / 2;
int* secondHalf = new int[secondHalfLength];
arraycopy(list, arraySize / 2, secondHalf, 0, secondHalfLength);
mergeSort(secondHalf, secondHalfLength);
// Merge firstHalf with secondHalf
merge(firstHalf, arraySize / 2, secondHalf, secondHalfLength,
list);
delete [] firstHalf;
delete [] secondHalf;
}
}
void merge(int list1[], int list1Size,
int list2[], int list2Size, int temp[])
{
int current1 = 0; // Current index in list1
int current2 = 0; // Current index in list2
int current3 = 0; // Current index in temp
while (current1 < list1Size && current2 < list2Size)
{
if (list1[current1] < list2[current2])
temp[current3++] = list1[current1++];
else
temp[current3++] = list2[current2++];
}
while (current1 < list1Size)
temp[current3++] = list1[current1++];
while (current2 < list2Size)
temp[current3++] = list2[current2++];
}
void arraycopy(int source[], int sourceStartIndex,
int target[], int targetStartIndex, int length)
{
for (int i = 0; i < length; i++)
{
target[i + targetStartIndex] = source[i + sourceStartIndex];
}
}
int main()
{
const int SIZE = 9;
int list[] = {1, 7, 3, 4, 9, 3, 3, 1, 2};
mergeSort(list, SIZE);
for (int i = 0; i < SIZE; i++)
cout << list[i] << " ";
return 0;
}
From my understanding, shouldn't there be another mergeSort function
call in order to merge sort the new firstHalf and secondHalf?
It is happening implicitly during the recursive call. When you reach these two lines:
delete [] firstHalf;
delete [] secondHalf;
It means that one call to mergeSort is completed. If this call belongs to merging a first half, then code starts from the line after, i.e. these lines:
// Merge sort the second half
int secondHalfLength = arraySize - arraySize / 2;
...
But, if this call belongs to merging of the second half, then the control goes back to the line just after that call, i.e. these lines:
// Merge firstHalf with secondHalf
merge(firstHalf, arraySize / 2, secondHalf, secondHalfLength,
list);
And everything if doing well as planned.

Is there an algorithm who finds permutations without repitition from a two dimensional list?

My input consists of a two dimensional list of integer values.
The the two dimensional list could hold three lists like the example below:
L1 L2 L3
--------------
1 11 111
2 22 222
3 33
4
The goal is to combine every element with every other element in the right order.
The result must be a two dimensional list, too.
Every list in the two dimensional list has a size of n, where n is the number of the input lists.
The following result would be generated:
L1: 1, 11, 111
L2: 1, 11, 222
L3: 1, 22, 111
L4: 1, 22, 222
L5: 1, 33, 111
L6: 1, 33, 222
L7: 2, 11, 111
L8: 2, 11, 222
L9: 2, 22, 111
.
.
.
L24: 4, 33, 222
It is necessary that everything has the right order like the example illustrates.
This problem can be solved recursively. The algorithm is pretty straightforward, the only tricky part is to avoid duplicated permutations when a list has duplicated numbers. A good way of doing this is to sort each list, so when picking integers from each list, it is very easy to tell if a particular number has been picked or not. The following is the implementation with your provided example as a test case. You can change a list's value to contain duplicated numbers and see that the output does not contain duplicated permutations.
public class ListPermutation {
public static List<List<Integer>> generateUniqueListPermutations(List<List<Integer>> lists) {
List<List<Integer>> permutations = new ArrayList<>();
if(lists == null || lists.size() == 0) {
return permutations;
}
//sort each input list
for(List<Integer> list : lists) {
Collections.sort(list);
}
permutationHelper(lists, permutations, new ArrayList<>(), 0);
return permutations;
}
private static void permutationHelper(List<List<Integer>> lists, List<List<Integer>> permutations, List<Integer> list, int idx) {
if(idx == lists.size()) {
permutations.add(new ArrayList<>(list));
return;
}
List<Integer> currList = lists.get(idx);
for(int i = 0; i < currList.size(); i++) {
if(i > 0 && currList.get(i) == currList.get(i - 1)) {
continue;
}
list.add(currList.get(i));
permutationHelper(lists, permutations, list, idx + 1);
list.remove(list.size() - 1);
}
}
public static void main(String[] args) {
List<Integer> list1 = new ArrayList<>(); list1.add(1); list1.add(2); list1.add(3); list1.add(4);
List<Integer> list2 = new ArrayList<>(); list2.add(11); list2.add(22); list2.add(33);
List<Integer> list3 = new ArrayList<>(); list3.add(111); list3.add(222);
List<List<Integer>> lists = new ArrayList<>(); lists.add(list1); lists.add(list2); lists.add(list3);
generateUniqueListPermutations(lists);
}
}

Transform this list using linq?

I'm trying to use LINQ to transform the following list. LINQ should multiply each element against the next as long as the product is less than 15. Additionally we should save the number of elements used to form the product.
int[] values = { 1, 3, 4, 2, 7, 14 }; //assume Largest value will never be >= 15
1x3x4 = 12
2x7 = 14
14 = 14
{ {12,3}, {14,2}, {14,1} }
My ultimate goal is to take the geometric average of a very large list of numbers. This is normally done by multiplying each element in the list together (1x3x4x2x7x14) then taking the nth root (in this case 1/6).
The obvious problem in using the "normal" method is that you will quickly find yourself using numbers beyond the maximum allowable number. You can workaround this by using the old divide and conquer method and with a little help from the natural log function.
I don't think there is something like that build into standard LINQ method library. But you can easily create your own extension method. I called it AggregateUntil:
public static class EnumerableExtensions
{
public static IEnumerable<TResult> AggregateUntil<TSource, TAccumulate, TResult>(
this IEnumerable<TSource> source,
TAccumulate seed,
Func<TAccumulate, TSource, TAccumulate> func,
Func<TAccumulate, bool> condition,
Func<TAccumulate, TResult> resultSelector
)
{
TAccumulate acc = seed;
TAccumulate newAcc;
foreach(var item in source)
{
newAcc = func(acc, item);
if(!condition(newAcc))
{
yield return resultSelector(acc);
acc = func(seed, item);
}
else
{
acc = newAcc;
}
}
yield return resultSelector(acc);
}
}
And now let's use it. First, take multiplications only, as long as they met < 15 condition:
var grouped
= values.AggregateUntil(1, (a,i) => a * i, a => a < 15, a => a).ToList();
Returns List<int> with 3 items: 12, 14, 14. That's what you need. But now lets take number of items which were aggregated into each multiplication. That's easy using anonymous type::
int[] values = { 1, 3, 4, 2, 7, 14 };
var grouped
= values.AggregateUntil(
new { v = 1, c = 0 },
(a, i) => new { v = a.v * i, c = a.c + 1 },
a => a.v < 15,
a => a).ToList(); ;
Returns exactly what you need:
My ultimate goal is to take the geometric average of a very large list of numbers.
Then just take the nth root of each number and multiply afterwards. Then you don't need to worry about splitting the list into groups:
double mean = 1.0;
foreach(int i in values)
{
mean *= Math.Pow(i, 1.0 / values.Length);
}
Which could also be done in Linq with Aggregate:
mean = values.Aggregate(1.0, (prev, i) => prev * Math.Pow(i, 1.0 / values.Length ));
Well my solution is not quite as elegant as #MarcinJuraszek, but it's fast and it works within your constraints.
int[] values = {1, 3, 4, 2, 7, 14};
int product = 1;
int elementsMultiplied = 0;
List<Tuple<int,int>> allElements = new List<Tuple<int,int>>();
for(int i = 0; i < values.Length ; i++)
{
product = product * values[i];
elementsMultiplied++;
if(i == values.Length - 1 || product * values[i+1] >= 15)
{
allElements.Add(new Tuple<int,int>(product, elementsMultiplied));
product = 1;
elementsMultiplied = 0;
}
}
foreach(Tuple<int,int> pair in allElements)
{
Console.WriteLine(pair.Item1 + "," + pair.Item2);
}

Find Second largest number in array at most n+log₂(n)−2 comparisons [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
The community reviewed whether to reopen this question 12 months ago and left it closed:
Original close reason(s) were not resolved
Improve this question
You are given as input an unsorted array of n distinct numbers, where n is a power of 2. Give an algorithm that identifies the second-largest number in the array, and that uses at most n+log₂(n)−2 comparisons.
Start with comparing elements of the n element array in odd and even positions and determining largest element of each pair. This step requires n/2 comparisons. Now you've got only n/2 elements. Continue pairwise comparisons to get n/4, n/8, ... elements. Stop when the largest element is found. This step requires a total of n/2 + n/4 + n/8 + ... + 1 = n-1 comparisons.
During previous step, the largest element was immediately compared with log₂(n) other elements. You can determine the largest of these elements in log₂(n)-1 comparisons. That would be the second-largest number in the array.
Example: array of 8 numbers [10,9,5,4,11,100,120,110].
Comparisons on level 1: [10,9] ->10 [5,4]-> 5, [11,100]->100 , [120,110]-->120.
Comparisons on level 2: [10,5] ->10 [100,120]->120.
Comparisons on level 3: [10,120]->120.
Maximum is 120. It was immediately compared with: 10 (on level 3), 100 (on level 2), 110 (on level 1).
Step 2 should find the maximum of 10, 100, and 110. Which is 110. That's the second largest element.
sly s's answer is derived from this paper, but he didn't explain the algorithm, which means someone stumbling across this question has to read the whole paper, and his code isn't very sleek as well. I'll give the crux of the algorithm from the aforementioned paper, complete with complexity analysis, and also provide a Scala implementation, just because that's the language I chose while working on these problems.
Basically, we do two passes:
Find the max, and keep track of which elements the max was compared to.
Find the max among the elements the max was compared to; the result is the second largest element.
In the picture above, 12 is the largest number in the array, and was compared to 3, 1, 11, and 10 in the first pass. In the second pass, we find the largest among {3, 1, 11, 10}, which is 11, which is the second largest number in the original array.
Time Complexity:
All elements must be looked at, therefore, n - 1 comparisons for pass 1.
Since we divide the problem into two halves each time, there are at most log₂n recursive calls, for each of which, the comparisons sequence grows by at most one; the size of the comparisons sequence is thus at most log₂n, therefore, log₂n - 1 comparisons for pass 2.
Total number of comparisons <= (n - 1) + (log₂n - 1) = n + log₂n - 2
def second_largest(nums: Sequence[int]) -> int:
def _max(lo: int, hi: int, seq: Sequence[int]) -> Tuple[int, MutableSequence[int]]:
if lo >= hi:
return seq[lo], []
mid = lo + (hi - lo) // 2
x, a = _max(lo, mid, seq)
y, b = _max(mid + 1, hi, seq)
if x > y:
a.append(y)
return x, a
b.append(x)
return y, b
comparisons = _max(0, len(nums) - 1, nums)[1]
return _max(0, len(comparisons) - 1, comparisons)[0]
The first run for the given example is as follows:
lo=0, hi=1, mid=0, x=10, a=[], y=4, b=[]
lo=0, hi=2, mid=1, x=10, a=[4], y=5, b=[]
lo=3, hi=4, mid=3, x=8, a=[], y=7, b=[]
lo=3, hi=5, mid=4, x=8, a=[7], y=2, b=[]
lo=0, hi=5, mid=2, x=10, a=[4, 5], y=8, b=[7, 2]
lo=6, hi=7, mid=6, x=12, a=[], y=3, b=[]
lo=6, hi=8, mid=7, x=12, a=[3], y=1, b=[]
lo=9, hi=10, mid=9, x=6, a=[], y=9, b=[]
lo=9, hi=11, mid=10, x=9, a=[6], y=11, b=[]
lo=6, hi=11, mid=8, x=12, a=[3, 1], y=11, b=[9]
lo=0, hi=11, mid=5, x=10, a=[4, 5, 8], y=12, b=[3, 1, 11]
Things to note:
There are exactly n - 1=11 comparisons for n=12.
From the last line, y=12 wins over x=10, and the next pass starts with the sequence [3, 1, 11, 10], which has log₂(12)=3.58 ~ 4 elements, and will require 3 comparisons to find the maximum.
I have implemented this algorithm in Java answered by #Evgeny Kluev. The total comparisons are n+log2(n)−2. There is also a good reference:
Alexander Dekhtyar: CSC 349: Design and Analyis of Algorithms. This is similar to the top voted algorithm.
public class op1 {
private static int findSecondRecursive(int n, int[] A){
int[] firstCompared = findMaxTournament(0, n-1, A); //n-1 comparisons;
int[] secondCompared = findMaxTournament(2, firstCompared[0]-1, firstCompared); //log2(n)-1 comparisons.
//Total comparisons: n+log2(n)-2;
return secondCompared[1];
}
private static int[] findMaxTournament(int low, int high, int[] A){
if(low == high){
int[] compared = new int[2];
compared[0] = 2;
compared[1] = A[low];
return compared;
}
int[] compared1 = findMaxTournament(low, (low+high)/2, A);
int[] compared2 = findMaxTournament((low+high)/2+1, high, A);
if(compared1[1] > compared2[1]){
int k = compared1[0] + 1;
int[] newcompared1 = new int[k];
System.arraycopy(compared1, 0, newcompared1, 0, compared1[0]);
newcompared1[0] = k;
newcompared1[k-1] = compared2[1];
return newcompared1;
}
int k = compared2[0] + 1;
int[] newcompared2 = new int[k];
System.arraycopy(compared2, 0, newcompared2, 0, compared2[0]);
newcompared2[0] = k;
newcompared2[k-1] = compared1[1];
return newcompared2;
}
private static void printarray(int[] a){
for(int i:a){
System.out.print(i + " ");
}
System.out.println();
}
public static void main(String[] args) {
//Demo.
System.out.println("Origial array: ");
int[] A = {10,4,5,8,7,2,12,3,1,6,9,11};
printarray(A);
int secondMax = findSecondRecursive(A.length,A);
Arrays.sort(A);
System.out.println("Sorted array(for check use): ");
printarray(A);
System.out.println("Second largest number in A: " + secondMax);
}
}
the problem is:
let's say, in comparison level 1, the algorithm need to be remember all the array element because largest is not yet known, then, second, finally, third. by keep tracking these element via assignment will invoke additional value assignment and later when the largest is known, you need also consider the tracking back. As the result, it will not be significantly faster than simple 2N-2 Comparison algorithm. Moreover, because the code is more complicated, you need also think about potential debugging time.
eg: in PHP, RUNNING time for comparison vs value assignment roughly is :Comparison: (11-19) to value assignment: 16.
I shall give some examples for better understanding. :
example 1 :
>12 56 98 12 76 34 97 23
>>(12 56) (98 12) (76 34) (97 23)
>>> 56 98 76 97
>>>> (56 98) (76 97)
>>>>> 98 97
>>>>>> 98
The largest element is 98
Now compare with lost ones of the largest element 98. 97 will be the second largest.
nlogn implementation
public class Test {
public static void main(String...args){
int arr[] = new int[]{1,2,2,3,3,4,9,5, 100 , 101, 1, 2, 1000, 102, 2,2,2};
System.out.println(getMax(arr, 0, 16));
}
public static Holder getMax(int[] arr, int start, int end){
if (start == end)
return new Holder(arr[start], Integer.MIN_VALUE);
else {
int mid = ( start + end ) / 2;
Holder l = getMax(arr, start, mid);
Holder r = getMax(arr, mid + 1, end);
if (l.compareTo(r) > 0 )
return new Holder(l.high(), r.high() > l.low() ? r.high() : l.low());
else
return new Holder(r.high(), l.high() > r.low() ? l.high(): r.low());
}
}
static class Holder implements Comparable<Holder> {
private int low, high;
public Holder(int r, int l){low = l; high = r;}
public String toString(){
return String.format("Max: %d, SecMax: %d", high, low);
}
public int compareTo(Holder data){
if (high == data.high)
return 0;
if (high > data.high)
return 1;
else
return -1;
}
public int high(){
return high;
}
public int low(){
return low;
}
}
}
Why not to use this hashing algorithm for given array[n]? It runs c*n, where c is constant time for check and hash. And it does n comparisons.
int first = 0;
int second = 0;
for(int i = 0; i < n; i++) {
if(array[i] > first) {
second = first;
first = array[i];
}
}
Or am I just do not understand the question...
In Python2.7: The following code works at O(nlog log n) for the extra sort. Any optimizations?
def secondLargest(testList):
secondList = []
# Iterate through the list
while(len(testList) > 1):
left = testList[0::2]
right = testList[1::2]
if (len(testList) % 2 == 1):
right.append(0)
myzip = zip(left,right)
mymax = [ max(list(val)) for val in myzip ]
myzip.sort()
secondMax = [x for x in myzip[-1] if x != max(mymax)][0]
if (secondMax != 0 ):
secondList.append(secondMax)
testList = mymax
return max(secondList)
public static int FindSecondLargest(int[] input)
{
Dictionary<int, List<int>> dictWinnerLoser = new Dictionary<int, List<int>>();//Keeps track of loosers with winners
List<int> lstWinners = null;
List<int> lstLoosers = null;
int winner = 0;
int looser = 0;
while (input.Count() > 1)//Runs till we get max in the array
{
lstWinners = new List<int>();//Keeps track of winners of each run, as we have to run with winners of each run till we get one winner
for (int i = 0; i < input.Count() - 1; i += 2)
{
if (input[i] > input[i + 1])
{
winner = input[i];
looser = input[i + 1];
}
else
{
winner = input[i + 1];
looser = input[i];
}
lstWinners.Add(winner);
if (!dictWinnerLoser.ContainsKey(winner))
{
lstLoosers = new List<int>();
lstLoosers.Add(looser);
dictWinnerLoser.Add(winner, lstLoosers);
}
else
{
lstLoosers = dictWinnerLoser[winner];
lstLoosers.Add(looser);
dictWinnerLoser[winner] = lstLoosers;
}
}
input = lstWinners.ToArray();//run the loop again with winners
}
List<int> loosersOfWinner = dictWinnerLoser[input[0]];//Gives all the elemetns who lost to max element of array, input array now has only one element which is actually the max of the array
winner = 0;
for (int i = 0; i < loosersOfWinner.Count(); i++)//Now max in the lossers of winner will give second largest
{
if (winner < loosersOfWinner[i])
{
winner = loosersOfWinner[i];
}
}
return winner;
}

Resources