Output heap as tree PHP - php

I have an array $heap = array(9, 9, 9, 8, 9, 9, 8, 9, 9, 9, 9, 9, 8, 8, 9, 7, 9, 8, 8, 9, 9,); and I want to output It like binary tree, when the two children nodes we can know by this formulas, $heap[$key*2+1] and second $heap[$key*2+2]. I try to run using foreach but getting error about undefined offset 21. Here is foreach:
foreach ($heap as $key => $value) {
echo $value;
if ($key != count($heap)) {
echo $heap[$key*2+1];
echo $heap[$key*2+2];
}
}
What I am doing wrong and how I can solve this?

I think the error in your check:
if ($key != count($heap)) {
echo $heap[$key*2+1];
echo $heap[$key*2+2];
}
Here may be a situation with $key*2+2 is out of bounds the array. Add the check for it too. Try to use
bool array_key_exists ( mixed $key , array $array ), something like this:
if ($key != count($heap)) {
if (array_key_exists($key*2 + 1, $heap)) echo $heap[$key*2 + 1];
if (array_key_exists($key*2 + 2, $heap) ) echo $heap[$key*2 + 2];
}
As you point out in comment, you are doubling the values in your code. This can be avoided by recursive function, something like this (pseudocode):
printArray(i, aHeap) {
if (i < count($heap)) {
echo $heap[i];
printArray(2*i + 1, aHeap);
printArray(2*i + 2, aHeap);
}
}

Its just a notice to stop it just define the offset by just checking if array key exists. Use the code below
foreach ($heap as $key => $value) {
echo $value;
if ($key != count($heap)) {
if (array_key_exists($key*2 + 1, $heap)){ echo $heap[$key*2 + 1];}
if (array_key_exists($key*2 + 2, $heap) ){ echo $heap[$key*2 + 2]; }
}
}
Hope this helps you

Actually you could do this without checking if key exists with a for:
for($i = 0, $m = count($heap) - 1; $i < $m - 2; $i++) {
echo $heap[$i+1];
echo $heap[$i+2];
}
This will assure that the key only goes to all indexes - 2, so in the last iteration $i + 2 will be the last key.

Related

PHP "for" loop that lists all even numbers and displays a sum of all odd numbers from an array

My task is to create a loop that displays all even numbers in a column and it also displays a sum of all odd numbers in an array.
So far I have made this:
<?php
$numbers = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
foreach ($numbers as $index=>$value) {
if ($value % 2 == 0)
echo "$value <br>";
}
?>
This code successfully displays a list of all even numbers. However, I still have to include a sum of all odd numbers that is displayed below the list of evens. For some reason, I am supposed to use a variable $sumOdd = 0.
How do I approach this from here on?
<?php
$numbers = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
$sumOdd = 0;
foreach ($numbers as $index=>$value) {
if ($value % 2 == 0)
echo "$value <br>";
} else {
$sumOdd += $value
}
}
echo $sumOdd;
To do it backwards: add all numbers, take out the even ones
<?php
$numbers = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
$sum = array_sum($numbers);
foreach ($numbers as $index=>$value) {
if ($value % 2 == 0)
echo "$value <br>";
$sum = $sum - $value;
}
echo 'odds: '. $sum;
?>
heyo,
the $sumOdd = 0; should be before the foreach, inside the foreach you will do another check and if it's odd you add the number on $sumOdd += $value
this is a short and cleaner answer :
$sumOdd = 0;
foreach (range(1, 10) as $number) {
if (0 === $number % 2) {
echo $number . '<br>';
continue;
}
$sumOdd += $number;
}
echo '<br> sumOdd = '.$sumOdd;
A better way to calculate the sum is to pass the result of array_filter to array_sum. By doing that, you separate the tasks (calculate, display) and the code becomes more clean and maintainable.
$numbers = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
$sumOdd = array_sum(array_filter($numbers, function($v) {
return $v % 2 !== 0;
}));

best way to count the bigger elements on the right and left side of an array

For example, in php
$arr = [9, 4, 3, 5, 2, 6];
then,
$output = [[0,0], [1,2], [2,2], [1,1], [4,1], [1,0]];
[0, 0] = the bigger elements of 9 is 0 on both side
[1, 2] = the bigger elements of 4 is 1 (9) on left and 2 (5, 6) on right side ... [ 9 > 4] - [ 5 > 4, 6 > 4 ]
[2, 2] = the bigger elements of 3 is 2 (9, 4) on the left and 2 (5, 6) on right side
[1, 1] = the bigger elements of 5 is 1 (9) on the left and 1 (6) on the right side
[4, 1] = the bigger elements of 2 (9, 4, 3, 5) is 4 on the left and 1 (6) on the right side
[1, 0] = the bigger elements of 6 is 1 (9) on the left and 0 (no elements after 6) on the right side
I want it in O(n log(n)), is it possible?
Looks like lots of answers already, you could do this with some of the array functions like some of the answers did, but since this is probably for your homework best to keep it simple. I keep track of whether it's left or right that should be incremented each iteration by using the $side variable.
$arr = [9, 4, 3, 5, 2, 6];
$results = [];
for ($x = 0; $x < count($arr); $x++) {
$results[$x] = [0,0];
$side = 0;
for ($y = 0; $y < count($arr); $y++) {
if ($arr[$y] > $arr[$x]) {
$results[$x][$side]++;
} elseif ($arr[$x] == $arr[$y]) {
$side = 1;
}
}
}
You need to loop through $arr to get each value, and then, in the loop, loop again through $arr to get the other values. Then, in the second loop, you build your output array by comparing both the value (to know if the number is, indeed, bigger) and the key (to know if it's on the left or the right).
$arr = array(9, 4, 3, 5, 2, 6);
$output = array();
foreach ($arr as $key=>$value) {
$out = array(0, 0);
foreach ($arr as $key2=>$value2) {
if ($key2 == $key) # If it's the same element
continue;
if ($value2 > $value) {
if ($key2 < $key)
$out[0]++;
else
$out[1]++;
}
}
$output[] = $out;
}
print_r($output);
See the output here.
Try this:
function fix_array($array) {
$return_array = array();
foreach ($array as $i => $value){
$left = array_slice($array, 0, $i);
$count_left = count(array_filter($left, function($var) use($value){
return $var > $value;
}));
$right = array_slice($array, $i + 1);
$count_right = count(array_filter($right, function($var) use($value){
return $var > $value;
}));
$return_array[] = [$count_left, $count_right];
}
return $return_array;
}
$arr = [9, 4, 3, 5, 2, 6];
$new_array = fix_array($arr);
print_r($new_array);
Simply compare it with left and right values. Try this:
$arr = [9, 4, 3, 5, 2, 6];
$total = count($arr);
$new_arr=array();
foreach ($arr as $key => $value) {
$left = 0;
$right = 0;
for ($i=0; $i < $total; $i++) {
if($key > $i && $arr[$i] > $arr[$key])
{
$left++;
}
elseif ($key < $i && $arr[$i] > $arr[$key]) {
$right++;
}
}
$new_arr[]=[$left,$right];
}
echo "<pre>";
print_r($new_arr);
Try the following code using array_walk()
<?php
$arr = [9, 4, 3, 5, 2, 6];
$finalArray =[];
array_walk($arr, function($value,$key) use(&$finalArray,&$arr) {
$param ['pre_val']=0;
$param ['post_val']=0;
$param ['current_index'] = $key;
$param ['current_value'] = $value;
$arr2 = $arr;
array_walk($arr2, function(&$value,$key) use(&$finalArray,&$param) {
if($key < $param['current_index']){
if($value > $param['current_value']){$param['pre_val'] ++;}
}else{
if($value > $param['current_value']){$param['post_val'] ++;}
}
$finalArray[$param['current_index']][0] = $param['pre_val'];
$finalArray[$param['current_index']][1] = $param['post_val'];
});
});
print_r($finalArray);

PHP - get the biggest possible integer match from an array of integers

I have an array of integers:
$intArr = [2, 4, 8, 16];
Given an $inputInt of 8, I need the biggest integer match from that array, which should return 8 of course.
The following function needs modification:
function getBiggestMatch($intputInt) {
$intArr = [2, 4, 8, 16];
foreach($intArr as $key => $value) {
if($key < $intputInt) {
$biggestMatch = $value;
}
}
return $biggestMatch;
}
$biggestMatch = getBiggestMatch(8); // should return 8, but returns 2 now
This function will return 2, because that is the first time that $key < $intInput. The desired result needs to be 8 from $intArr.
This should work for you:
First of all, just to make sure, we reindex the array and sort it with array_values() and sort().
After this we loop through the array with a while loop and always check if the next key exists and the current key => element is smaller than the input $number:
Check if next element exists Check that current array value is smaller than the input
┌─────────┴───────┐ ┌─────────┴────────┐
while(isset($arr[$key+1]) && $arr[$key] < $number)
$key++; //→ Go to next key, which must exist
Now after the while loop the $key => value can point to either a value which is the same as the input or higher. Now we just check the following:
value == input ($arr[$key] == $number) => return current value (return $arr[$key];)
distance between current value to input ($arr[$key] - $number) is smaller (<) than the distance between the last value to the input ($number - $arr[$key-1]) => return current value (return $arr[$key];)
else => return last value (return $arr[$key-1];)
Code:
<?php
function getNearestMatch($number) {
$arr = array_values([2, 4, 8, 16, 23]);
sort($arr);
$key = 0;
while(isset($arr[$key+1]) && $arr[$key] < $number)
$key++;
if($arr[$key] == $number || !isset($arr[$key-1]) || $arr[$key] - $number < $number - $arr[$key-1])
return $arr[$key];
else
return $arr[$key-1];
}
//To test it we pass some values to the function
foreach([0, 2, 3, 4, 8, 12, 22, 23, 24] as $v)
echo getNearestMatch($v) . PHP_EOL;
?>
output:
2
2
2
4
8
8
23
23
23
Compare the $value, not the $key. This is returning 8.
<?php
function getBiggestMatch($intputInt) {
$intArr = [2, 4, 8, 16];
foreach($intArr as $key => $value) {
if($value <= $intputInt) {
$biggestMatch = $value;
}
}
return $biggestMatch;
}
echo getBiggestMatch(8); // should return 8, but returns 2 now
?>
You need to do double check because if your array does not have an order the last minor value will return:
function getBiggestMatch($intputInt) {
$intArr = [4, 8, 2, 16];
$maxInt = false;
foreach($intArr as $key => $value) {
if($intputInt >= $value && $maxInt < $value) $maxInt = $value;
}
return $maxInt;
}
echo getBiggestMatch(12);
ideone code
EDIT
Following the #Rizier123's recommendation, the function needs to find the max closest value (I had not understood very well the original question), this could be a possible solution:
function getBiggestMatch($intputInt) {
$intArr = [4, 8, 2, 16];
$maxInt = false;
$diference = 0;
foreach($intArr as $key => $value) {
if( $diference == 0 || ($diference > 0 && abs($intputInt - $value) < $diference) ) {
$maxInt = $value;
$diference = abs($intputInt - $value);
}
}
return $maxInt;
}
echo getBiggestMatch(15);
ideone code

Remove duplicates of array

I was just going through these questions for PHP and got stuck at one of them. The question is:
You have a PHP 1 dimensional array. Please write a PHP function that
takes 1 array as its parameter and returns an array. The function must
delete values in the input array that shows up 3 times or more?
For example, if you give the function
array(1, 3, 5, 2, 6, 6, 6, 3, 1, 9)the function will returnarray(1, 3, 5, 2, 3, 1, 9)
I was able to check if they are repeating themselves but I apply it to the array I am getting as input.
function removeDuplicate($array){
$result = array_count_values( $array );
$values = implode(" ", array_values($result));
echo $values . "<br>";
}
$qArray = array(1, 3, 5, 2, 6, 6, 6, 3, 1, 9);
removeDuplicate($qArray);
One more thing, we cannot use array_unique because it includes the value which is repeated and in question we totally remove them from the current array.
Assuming the value may not appear 3+ times anywhere in the array:
$array = array(1, 3, 5, 2, 6, 6, 6, 3, 1, 9);
// create array indexed by the numbers to remove
$remove = array_filter(array_count_values($array), function($value) {
return $value >= 3;
});
// filter the original array
$results = array_values(array_filter($array, function($value) use ($remove) {
return !array_key_exists($value, $remove);
}));
If values may not appear 3+ times consecutively:
$results = [];
for ($i = 0, $n = count($array); $i != $n;) {
$p = $i++;
// find repeated characters
while ($i != $n && $array[$p] === $array[$i]) {
++$i;
}
if ($i - $p < 3) {
// add to results
$results += array_fill(count($results), $i - $p, $array[$p]);
}
}
This should work :
function removeDuplicate($array) {
foreach ($array as $key => $val) {
$new[$val] ++;
if ($new[$val] >= 3)
unset($array[$key]);
}
return $array;
}
run this function i hope this help..
function removeDuplicate($array){
$result = array_count_values( $array );
$dub = array();
$answer = array();
foreach($result as $key => $val) {
if($val >= 3) {
$dub[] = $key;
}
}
foreach($array as $val) {
if(!in_array($val, $dub)) {
$answer[] = $val;
}
}
return $answer;
}
You can use this function with any number of occurrences you want - by default 3
function removeDuplicate($arr, $x = 3){
$new = $rem = array();
foreach($arr as $val) {
$new[$val]++;
if($new[$val]>=$x){
$rem[$val]=$new[$val];
}
}
$new = array_keys(array_diff_key($new, $rem));
return $new;
}
I think it is getting correct output. just try once.
$array=array(1,2,3,7,4,4,3,5,5,6,7);
$count=count($array);
$k=array();
for($i=0;$i<=$count;$i++)
{
if(!in_array($array[$i],$k))
{
$k[]=$array[$i];
}
}
array_pop($k);
print_r($k);
in this first $k is an empty array,after that we are inserting values into $k.
You should use array_unique funciton.
<?php
$q = array(1, 3, 5, 2, 6, 6, 6, 3, 1, 9);
print_r(array_unique($q));
?>
Try it and let me know if it worked.

Finding frequent sequence of numbers in an array

Array (3, 5, 1, 3, 5, 48, 4, 7, 13, 55, 65, 4, 7, 13, 32)
the frequent sequence of numbers will be (3, 5) f=2 + (4, 7, 13) f=2
any Algorithm or Pseudo code to find that ?
Update(1):
if (7, 13) also occurrence it will be included in the longest one by update its frequency so
(4, 7, 13) f=3 and so on...
Update(2):
in case of (1,2,3,4,1,2,3,4,1,2,7,8,7,8,3,4,3,4,1,2) the output should be (1,2,3,4) & (3,4,1,2)
& (7,8) , to make it clear consider each number as a word and you want to find most frequent phrases
so it is common to see same word(s) in a lot of phrases but if any phrase was sub-string for any other
phrase(s) should not be consider as a phrase but will update frequency of each phrase includes it
** EDIT ** : slightly better implementation, now also returns frequences and has a better sequence filter.
function getFrequences($input, $minimalSequenceSize = 2) {
$sequences = array();
$frequences = array();
$len = count($input);
for ($i=0; $i<$len; $i++) {
$offset = $i;
for ($j=$i+$minimalSequenceSize; $j<$len; $j++) {
if ($input[$offset] == $input[$j]) {
$sequenceSize = 1;
$sequence = array($input[$offset]);
while (($offset + $sequenceSize < $j)
&& ($input[$offset+$sequenceSize] == $input[$j+$sequenceSize])) {
if (false !== ($seqIndex = array_search($sequence, $frequences))) {
// we already have this sequence, since we found a bigger one, remove the old one
array_splice($sequences, $seqIndex, 1);
array_splice($frequences, $seqIndex, 1);
}
$sequence[] = $input[$offset+$sequenceSize];
$sequenceSize++;
}
if ($sequenceSize >= $minimalSequenceSize) {
if (false !== ($seqIndex = array_search($sequence, $sequences))) {
$frequences[$seqIndex]++;
} else {
$sequences[] = $sequence;
$frequences[] = 2; // we have two occurances already
}
// $i += $sequenceSize; // move $i so we don't reuse the same sub-sequence
break;
}
}
}
}
// remove sequences that are sub-sequence of another frequence
// ** comment this to keep all sequences regardless **
$len = count($sequences);
for ($i=0; $i<$len; $i++) {
$freq_i = $sequences[$i];
for ($j=$i+1; $j<$len; $j++) {
$freq_j = $sequences[$j];
$freq_inter = array_intersect($freq_i, $freq_j);
if (count($freq_inter) != 0) {
$len--;
if (count($freq_i) > count($freq_j)) {
array_splice($sequences, $j, 1);
array_splice($frequences, $j, 1);
$j--;
} else {
array_splice($sequences, $i, 1);
array_splice($frequences, $i, 1);
$i--;
break;
}
}
}
}
return array($sequences, $frequences);
};
Test case
header('Content-type: text/plain');
$input = array(3, 5, 1, 3, 5, 48, 4, 7, 13, 55, 3, 5, 65, 4, 7, 13, 32, 5, 48, 4, 7, 13);
list($sequences, $frequences) = getFrequences($input);
foreach ($sequences as $i => $s) {
echo "(" . implode(',', $s) . ') f=' . $frequences[$i] . "\n";
}
** EDIT ** : here's an update to the function. It was almost completely rewritten... tell me if this is what you were looking for. I also added a redundancy check to prevent counting the same sequence, or subsequence, twice.
function getFrequences2($input, $minSequenceSize = 2) {
$sequences = array();
$last_offset = 0;
$last_offset_len = 0;
$len = count($input);
for ($i=0; $i<$len; $i++) {
for ($j=$i+$minSequenceSize; $j<$len; $j++) {
if ($input[$i] == $input[$j]) {
$offset = 1;
$sub = array($input[$i]);
while ($i + $offset < $j && $j + $offset < $len) {
if ($input[$i + $offset] == $input[$j + $offset]) {
array_push($sub, $input[$i + $offset]);
} else {
break;
}
$offset++;
}
$sub_len = count($sub);
if ($sub_len >= $minSequenceSize) {
// $sub must contain more elements than the last sequence found
// otherwise we will count the same sequence twice
if ($last_offset + $last_offset_len >= $i + $sub_len) {
// we already saw this sequence... ignore
continue;
} else {
// save offset and sub_len for future check
$last_offset = $i;
$last_offset_len = $sub_len;
}
foreach ($sequences as & $sequence) {
$sequence_len = count($sequence['values']);
if ($sequence_len == $sub_len && $sequence['values'] == $sub) {
//echo "Found add-full ".var_export($sub, true)." at $i and $j...\n";
$sequence['frequence']++;
break 2;
} else {
if ($sequence_len > $sub_len) {
$end = $sequence_len - $sub_len;
$values = $sequence['values'];
$slice_len = $sub_len;
$test = $sub;
} else {
$end = $sub_len - $sequence_len;
$values = $sub;
$slice_len = $sequence_len;
$test = $sequence['values'];
}
for ($k=0; $k<=$end; $k++) {
if (array_slice($values, $k, $slice_len) == $test) {
//echo "Found add-part ".implode(',',$sub)." which is part of ".implode(',',$values)." at $i and $j...\n";
$sequence['values'] = $values;
$sequence['frequence']++;
break 3;
}
}
}
}
//echo "Found new ".implode(',',$sub)." at $i and $j...\n";
array_push($sequences, array('values' => $sub, 'frequence' => 2));
break;
}
}
}
}
return $sequences;
};
In Python3
>>> from collections import Counter
>>> count_hash=Counter()
>>> T=(3, 5, 1, 3, 5, 48, 4, 7, 13, 55, 65, 4, 7, 13, 32)
>>> for i in range(2,len(T)+1):
... for j in range(len(T)+1-i):
... count_hash[T[j:j+i]]+=1
...
>>> for k,v in count_hash.items():
... if v >= 2:
... print(k,v)
...
(3, 5) 2
(4, 7, 13) 2
(7, 13) 2
(4, 7) 2
Do you need to filter the (7,13) and the (4,7) out? What happens if there was also (99, 7, 14) in the sequence?
a Counter is just like a hash used to keep track of the number of times we see each substring
The two nested for loops produce all the substrings of T, using count_hash to accumulate the count of each substring.
The final for loop filters all those substrings that only occurred once
Here is a version with a filter
from collections import Counter
def substrings(t, minlen=2):
tlen = len(t)
return (t[j:j+i] for i in range(minlen, tlen+1) for j in range(tlen+1-i))
def get_freq(*t):
counter = Counter(substrings(t))
for k in sorted(counter, key=len):
v=counter[k]
if v < 2:
del counter[k]
continue
for t in substrings(k):
if t in counter:
if t==k:
continue
counter[k]+=counter[t]-v
del counter[t]
return counter
print(get_freq(3, 5, 1, 3, 5, 48, 4, 7, 13, 55, 65, 4, 7, 13, 32, 4, 7))
print(get_freq(1,2,3,4,1,2,3,4,1,2,7,8,7,8,3,4,3,4,1,2))
the output is
Counter({(4, 7, 13): 3, (3, 5): 2})
Counter({(1, 2, 3, 4, 1, 2): 8, (7, 8): 2}) # Is this the right answer?
Which is why I asked how the filtering should work for the sequence I gave in the comments
Ok, just to start off the discussion.
Create another array/map, call this
weightage array.
Start iterating on the values array.
For each value in
values array,increment the
corresponding position in weightage
array. Eg: for 3 increase
weightage[3]++, for 48
weightage[48]++.
After the iteration the weightage array contains
repetitions

Categories