Bubble Sort in O(n)? - bubble-sort

I know traditionally bubble sort is n^2 time complexity, but I wrote my own implementation of it and to me it looks O(n). I don't see how it can be n^2 because I don't have any nested loops. Even when I call it recursively in a loop, I break the loop right after the recursion.
def bubblesort(x):
index = 0
while index <= len(x) - 2:
if x[index + 1] < x[index]:
temp = x[index + 1]
x[index + 1] = x[index]
x[index] = temp
else:
index += 1
print(index)
index = 0
while index <= len(x) - 2:
if x[index + 1] < x[index]:
bubblesort(x)
break
else:
index += 1

If I unwrap the tail recursion it's clearly a nested loop for O(N^2)
def bubblesort(x):
do
index = 0
while index <= len(x) - 2:
if x[index + 1] < x[index]:
temp = x[index + 1]
x[index + 1] = x[index]
x[index] = temp
else:
index += 1
print(index)
index = 0
while index <= len(x) - 2 and x[index + 1] >= x[index]:
index += 1
while index <= len(x) - 2 and x[index + 1] < x[index]

Related

The Solution About Foobar Challenge "Find The Access Code"

I am challenging google foobar currently and met this question.
A "lucky triple" is a tuple (x, y, z) where x divides y and y divides z, such as (1, 2, 4).
Write a function solution(l) that takes a list of positive integers l and counts the number of "lucky triples" of (li, lj, lk) where the list indices meet the requirement i < j < k. The length of l is between 2 and 2000 inclusive. The elements of l are between 1 and 999999 inclusive. The solution fits within a signed 32-bit integer. Some of the lists are purposely generated without any access codes to throw off spies, so if no triples are found, return 0.
I have try some other method and keep failing on the 5th test. Somehow I found solution from Google Foobar Challenge 3 - Find the Access Codes , but I don't understand why my code don't work.
Here's the code that I refer the method from "Find the access codes" Google Foobar challenge
def solution(l):
i = 0
indexes = []
inputSize = len(l)
keyCounter = 0
while i < inputSize-1:
temp = i+1
matches = []
while temp < inputSize:
if(l[temp] % l[i] == 0):
matches.append(temp)
temp +=1
indexes.append(matches)
i+=1
m = 0
while m < len(indexes):
n=0
temp = indexes[m]
while n < len(temp)-1:
keyCounter += len(list(set(indexes[m]).intersection(indexes[temp[n]])))
n +=1
m+=1
return keyCounter
and my original attemps :
def solution(l):
i = 0
j = 1
k = 2
keyCounter = 0
while i < len(l)-2:
if(l[j] % l[i] == 0):
if(l[k] % l[j] == 0):
keyCounter +=1
if(k < len(l)-1):
k += 1
elif(j < len(l)-2):
j += 1
k = j + 1
else:
i += 1
j = i + 1
k = j + 1
else:
if(j < len(l)-2):
j += 1
k = j + 1
else:
i += 1
j = i + 1
k = j + 1
return keyCounter

nested for loop optimisation

I have this expression
for (size_t i = 0; i < expression.size(); i++){
for (size_t j = i + 1; j < expression.size(); j++){
result += (expression.at(j) - expression.at(i));
}
result += (g - expression.at(i));
}
return result;
in the vector expression we have for example [1,2,3]. I am trying to get something like:
f1=[(2-1)+(3-1)]
r1 = g-1
h1 = r1+f1
f2=[3-2]
r2 = g-2
h2 = r2+f2
f3 = 0
r3 = g-3
h3 = r3+f3
then h1+h2+h3
What i am doing right now is in Θ(n^2). Is there a way to make it faster even without for loops?
Addition is commutative and associative so the operations can be reordered and grouped without changing the final result. (Note: not taking into account possible overflows in the intermediate calculations, which may be affected by the order and grouping of operations.)
In pseudo-code with n = expression.size() and x[k] = expression.at(k) the original code can be broken down as follows, with the intermediate results indicated in comments.
a = b = c = d = 0
for i = 0 to (n-1)
for j = (i+1) to (n-1)
a += x[j]
// == x[i+1] + x[i+2] + ... x[n-1]
// a == 0 * x[0] + 1 * x[1] + 2 * x[2] + 3 * x[3] + ... + (n-1) * x[n-1]
for i = 0 to (n-1)
for j = (i+1) to (n-1)
b += x[i];
// == (n-i-1) * x[i]
// b == (n-1) * x[0] + (n-2) * x[1] + ... + 2 * x[n-3] + 1 * x[n-2]
for i = 0 to (n-1)
c += g
// c == n * g
for i = 0 to (n-1)
d += expression.at(i))
// d == x[0] + x[1] + ... + x[n-1]
result = c + a - b - d
= n * g
+ (0 - (n-1) - 1) * x[0]
+ (1 - (n-2) - 1) * x[1]
+ ...
+ ((n-2) - 1 - 1) * x[n-2]
+ ((n-1) - 0 - 1) * x[n-1]
The latter result can be calculated directly from that formula, with one single O(n) loop.

Mathematical derivation of O(n^3) complexity for general three nested loops

Time complexity of nested for-loop goes into the mathematical derivation through summation of two loops O(n2) complexity.
I tried an exercise to derive how i can get O(n3) for the following examples of three nested loops.
Simple three nested loops.
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
for (int k = 1; k <= n; k++)
print(i * k * j);
Summation is
= (n + n + n + .... + n) + (n + n + n + ... + n) + ... + (n + n + n + ... + n)
= n^2 + n^2 + .... + n^2
n times n^2 = O(n^3)
Three nested loops not run n times from beginning
for(int i = 1; i <= n; i++)
for(int j = i; j <= n; j++)
for(int k = j; k <= n; k++)
print(i *j * k)
The above is a pretty common form of nested loops and i believe the summation would be something as follows
= (n + (n -1) + (n -2) + (n - 3) + ... + 1)
+ ((n -1) + (n - 2) + (n - 3) +... + 1)
+ ((n -2) + (n -3) + (n - 4) + ... + 1)
+ ...
+ ((n - (n -2)) + 1)
= n(n - 1) /2 + (n-1) (n -2) / 2 + (n-2)(n-3)/2 + .... + 1
=
From here i am slightly not sure if my logic is correct . I believe
each of the above evaluate to a polynomial whose greatest value is n2 and as thats what we care about in time complexity, the above equation breaks down to.
= n^2 + n^2 + n^2 +... +n^2
= which is n times n^2 = O(n^3).
Is my assumption correct?
Three nested loops not run n times from end
for(int i = 1; i <= n; i++)
for(int j = 1; j <= i; j++)
for(int k = 1; k <= j; k++)
print(i *j * k)
If the above was a two nested loop the summation would have been 1 + 2 + 3 + 4 + ... + n. However, for the three nested occurence i am deducing it to be
= 1 + (1 + 2) + (1 + 2 + 3) + (1 + 2 + 3) + (1 + 2 + 3 + .... + n)
From here i am not sure how to derive O(n^3) or how the above summation would be further simplified.
Using the fact that:
1+2+3+...+i =i*(i+1)/2, the summation above can be written as:
1*(1+1)/2 + 2*(2+1)/2 + ... + n*(n+1)/2.
Obviously i*(i+1) > i^2, therefore:
1*(1+1)/2 + 2*(2+1)/2 + ... + n*(n+1)/2 > (1^2+...+ n^2)/2, as we know:
1^2+...+n^2 = n^3/3 + n^2/2 + n/6 (can prove this by induction).
Therefore, the original sum S is bigger than:
n^3/6 + n^2/4 + n/12, which is O(n^3).

Understanding Bubble Sort (Algorithm)

I'm trying to understand the simplest of all swapping algorithms, the bubblesort. Yet I seem to be confused on the steps of actually swapping values, for instance consider the code :
void bubbleSort(int arr[], int n) {
bool swapped = true;
int j = 0;
int tmp;
while (swapped) {
swapped = false;
j++;
for (int i = 0; i < n - j; i++) {
if (arr[i] > arr[i + 1]) {
tmp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = tmp;
swapped = true;
}
}
}
}
Let's say I have a list of numbers like this:
7 1 3 4 6 3 5
And I want to swap the first two values, 7 and 1:
By my logic, this is how I'm understanding this code:
set a temp variable equal to 7, so
temp = 7;
set 7 equal to the next value, so
7 = 1; ?
The list at the moment is:
1 1 3 4 6 3 5
Where temp = 7
Now set 1 equal to temp, which is 7?
1 = temp;
So the list is now:
1 7 3 4 6 3 5
Is my understanding correct on this?
First, you do seem to be on the right track.
Some tips to help you progress further on your journey.
Learn the standard template library. There is a function called swap which does exactly what it says on the tin.
Secondly use containers. They are less error prone than C-style arrays.
Finally here is bubble sort explained via the medium of folk dancing.
In this code snippet
if (arr[i] > arr[i + 1]) {
tmp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = tmp;
swapped = true;
}
you need to swap two objects arr[i] and arr[i + 1]
If you write directly
arr[i] = arr[i + 1];
then the two objects will have the same value that is the previous value of arr[i] will be lost.
So at first you need to preserve this value somewhere. To do so there is declared an auxiliary intermediate variable tmp
So at first the value of arr[i] is preserved in variable tmp
Let's assume that arr[i] has value 7 and arr[i + 1] has value 1.
tmp = arr[i];
Now tmp and arr[i] has the same value 7.
Then arr[i] is overwritten by the value of arr[i + 1]
arr[i] = arr[i + 1];
Now these two variables have the same value 1 that is the value of arr[i + 1]
We have tmp is equal to 7, arr[i] and arr[i + 1] are equal to 1
However the previous value of arr[i] was preserved in variable tmp
So now this value is assigned to arr[i + 1]
arr[i + 1] = tmp;
And we are getting arr[i + 1] is eqal to 7 and arr[i] is equal tp 1
Thus the values were swapped.

why a heap node of index i (starting from 1) and its height h satisfy (2^h)*i <= n < (2^(h+1)*i) where n is the heap size?

why a heap node of index i (starting from 1) and its height h satisfy (2^h)*i <= n < (2^(h+1)*i) where n is the heap size?
Case N:1
2^h <= 1 <= 2^(h+1)
Note height of node is log(n) = log(1) = 0
= 2^0 <= 1 <= 2^(0+1)
= 1 <= 1 <= 2
So you can see its true for case n = 1
Let replace h = log(n) into the original question
= 2^h <= n <= 2^(h+1)
= 2^(log(n)) <= n <= 2^(log(n)+1) #replace n = log(n)
= n <= n <= 2^log(n) * 2^1 #exponents property
= n <= n <= 2n
Note the index 'i' cancels out if we divide by 'i' on each side.