Related
I'm trying to write a program whose input is an array of integers, and its size. This code has to delete each element which is smaller than the element to the left. We want to find number of times that we can process the array this way, until we can no longer delete any more elements.
The contents of the array after we return are unimportant - only the return value is of interest.
For example: given the array [10, 9, 7, 8, 6, 5, 3, 4, 2, 1], the function should return 2, because:
[10,9,7,8,6,5,3,4,2,1] → [10,8,4] → [10]
For example: given the array [1,2,3,4], the function should return 0, because
No element is larger than the element to its right
I want each element to remove the right element if it is more than its right element. We get a smaller array. Then we repeat this operation again. Until we get to an array in which no element can delete another element. I want to calculate the number of steps performed.
int Mafia(int n, vector <int> input_array)
{
int ptr = n;
int last_ptr = n;
int night_Count = 0;
do
{
last_ptr = ptr;
ptr = 1;
for (int i = 1; i < last_ptr; i++)
{
if (input_array[i] >= input_array[i - 1])
{
input_array[ptr++] = input_array[i];
}
}
night_Count++;
} while (last_ptr > ptr);
return night_Count - 1;
}
My code works but I want it to be faster.
Do you have any idea to make this code faster, or another way that is faster than this?
Here is a O(NlogN) solution.
The idea is to iterate over the array and keep tracking candidateKillers which could kill unvisited numbers. Then we find the killer for the current number by using binary search and update the maximum iterations if needed.
Since we iterate over the array which has N numbers and apply log(N) binary search for each number, the overall time complexity is O(NlogN).
Alogrithm
If the current number is greater or equal than the number before it, it could be a killer for numbers after it.
For each killer, we keep tracking its index idx, the number of it num and the iterations needed to reach that killer iters.
The numbers in the candidateKillers by its nature are non-increasing (see next point). Therefore we can apply binary search to find the killer of the current number, which is the one that is a) the closest to the current number b) greater than the current number. This is implemented in searchKiller.
If the current number will be killed by a number in candidateKillers with killerPos, then all candidate killers after killerPos are outdated, because those outdated killers will be killed before the numbers after the current number reach them. If the current number is greater than all candidateKillers, then all the candidateKillers can be discarded.
When we find the killer of the current number, we increase the iters of the killer by one. Because from now on, one more iteration is needed to reach that killer where the current number need to be killed first.
class Solution {
public:
int countIterations(vector<int>& array) {
if (array.size() <= 1) {
return 0;
}
int ans = 0;
vector<Killer> candidateKillers = {Killer(0, array[0], 1)};
for (auto i = 1; i < array.size(); i++) {
int curNum = array[i];
int killerPos = searchKiller(candidateKillers, curNum);
if (killerPos == -1) {
// current one is the largest so far and all candidateKillers before are outdated
candidateKillers = {Killer(i, curNum, 1)};
continue;
}
// get rid of outdated killers
int popCount = candidateKillers.size() - 1 - killerPos;
for (auto j = 0; j < popCount; j++) {
candidateKillers.pop_back();
}
Killer killer = candidateKillers[killerPos];
ans = max(killer.iters, ans);
if (curNum < array[i-1]) {
// since the killer of the current one may not even be in the list e.g., if current is 4 in [6,5,4]
if (killer.idx == i - 1) {
candidateKillers[killerPos].iters += 1;
}
} else {
candidateKillers[killerPos].iters += 1;
candidateKillers.push_back(Killer(i, curNum, 1));
}
}
return ans;
}
private:
struct Killer {
Killer(int idx, int num, int iters)
: idx(idx), num(num), iters(iters) {};
int idx;
int num;
int iters;
};
int searchKiller(vector<Killer>& candidateKillers, int n) {
int lo = 0;
int hi = candidateKillers.size() - 1;
if (candidateKillers[0].num < n) {
return -1;
}
int ans = -1;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
if (candidateKillers[mid].num > n) {
ans = mid;
lo = mid + 1;
} else {
hi = mid - 1;
}
}
return ans;
}
};
int main() {
vector<int> array1 = {10, 9, 7, 8, 6, 5, 3, 4, 2, 1};
vector<int> array2 = {1, 2, 3, 4};
vector<int> array3 = {4, 2, 1, 2, 3, 3};
cout << Solution().countIterations(array1) << endl; // 2
cout << Solution().countIterations(array2) << endl; // 0
cout << Solution().countIterations(array3) << endl; // 4
}
You can iterate in reverse, keeping two iterators or indices and moving elements in place. You don't need to allocate a new vector or even resize existing vector. Also a minor, but can replace recursion with loop or write the code the way compiler likely to do it.
This approach is still O(n^2) worst case but it would be faster in run time.
I'm asked to count the exact number of operations cost for this function. I've used op to keep track of it. The recursive call should be the number of operations performed by that function call (I'm not sure if I'm doing that right in my code). My question is, how do I formulate a mathematical equation to match the number of operations. I've commented beside each line to annotate the cost of operations. From that, i get 2n^2 - n + 6. But this don't match with my output.
// PARAM: arr is array to be sorted, n is size of array, i should initially = 0
int ssort(int arr[], int n, int i)
{
int op = 0;
if (i < n-1) //1
{
// Find and swap smallest remaining
int next = i + 1; //1
int smallest = i; //1
while (next < n) //i+1
{
if (arr[next] < arr[smallest]) //i
{
smallest = next; //i
}
next++; //i
op += 4; altogether 4 ops
}
op++; //while loop terminates
// Swap i with smallest
int temp = arr[i]; //1
arr[i] = arr[smallest]; //1
arr[smallest] = temp; //1
op += ssort(arr, n, i + 1);
}
op+=6;
return op;
}
To find the formula, you can simply test out multiple cases, and interpolate a formula from it.
// set up an empty array of length 10
int arr[10];
// set up a for loop for i = 1, 2, 3...
for(auto i : {1, 2, 3, 4, 5, 6, 7, 8, 9})
{
std::cout << ssort(arr, i, 0) << ", ";
}
This prints:
6, 17, 32, 51, 74, 101, 132, 167, 206, 249,
Now put that data in a polynomial interpolation calculator, you will get:
I have to create a function that will find the longest consecutive sequence of integers in an array.
The array is this:
{1,2,3,4,4,4,5,7,9,10}
Note: repeated numbers in a sequence are skipped.
Here is my code:
int longestSequence(const int arr[], int size)
{
int currentSeq = 1;//set to 1 because all sequences are at least 1
int maxSeq;
for (int i = 1; i < size; i++)
{
if (arr[i] == arr[i-1]);//skip repeated numbers
else if (arr[i-1] == (arr[i] - 1))//check if index is 1 greater than last
{
currentSeq++;
}
else //if not reset and hold last sequence value as max
{
maxSeq = currentSeq;
currentSeq = 1;
}
}
if (currentSeq > maxSeq) //if the last index of the array was last in the sequence
{
maxSeq = currentSeq;
}
return maxSeq;
}
My code keeps returning 2 for this array but obviously it should be 5.
Any help would be appreciated.
You have 3 sequences in your array:
1, 2, 3, 4, 4, 4, 5 which has 5 consecutive numbers.
5, 7 which is not consecutive and will return 1.
7, 9 which will also return one.
9, 10 which has 2 consecutives and will return two.
In your loop you're doing this:
for (int i = 1; i < size; i++)
{
if (arr[i] == arr[i-1]);//skip repeated numbers
else if (arr[i-1] == (arr[i] - 1))//check if index is 1 greater than last
{
currentSeq++;
}
else //if not reset and hold last sequence value as max
{
maxSeq = currentSeq; // <-- You're resetting your maxSequence even if
// currentSeq is smaller.
currentSeq = 1;
}
}
Change your loop as following:
for (int i = 1; i < size; i++)
{
if (arr[i] == arr[i-1])
continue; //It is a good practice to skip the rest of the loop's
//checks if you already know you're not gonna need them.
//The continue keyword skips only the current iteration of
//the loop and improves intent readability.
else if (arr[i-1] == (arr[i] - 1))//check if index is 1 greater than last
{
currentSeq++;
}
else //if not reset and hold last sequence value as max
{
currentSeq = 1; //This line stays.
//You only want to reset it under these specific conditions.
}
if (currentSeq > maxSeq) // Now you'll get the last sequence as well.
maxSeq = currentSeq;
}
You can remove that check outside of the loop and go straight to the return. The last currentSeq will be properly registred if it is bigger than maxSeq.
Also, when I did this change I got a compile error because the new if (currentSeq > maxSeq) inside the loop tries to read maxSeq before it is ever set. So change the declaration of maxSeq to int maxSeq = 0.
With these changes I got the expected value of 5.
By running your program in a debugger with the sample array you provided, I determined that the variable currentSeq does indeed reach the value 5 and this value 5 is correctly written to maxSeq. However, later in the program, maxSeq gets overwritten by the value 1 and then 2.
Before overwriting maxSeq, you must determine whether it already contains a higher value than currentSeq. You already do this in one case, when you have reached the end of the array. But you don't do this in the other case.
For such a comparison to work, you must also initialize maxSeq, not just currentSeq. Otherwise, maxSeq may contain a very large number and always be larger than currentSeq.
#include <iostream>
void Log(int idx, int currentSeq, int maxSeq) {
printf("current[%d] = %d, max[%d] = %d\n", idx, currentSeq, idx, maxSeq);
}
int LongestSequence(int* arr, int size) {
int currentSeq = 1;
int maxSeq = 1; // max should be initialized as well
Log(0, currentSeq, maxSeq);
for (int i = 1; i < size; i++) {
if (arr[i] == arr[i - 1]) {
} else if (arr[i - 1] == (arr[i] - 1)) {
currentSeq++;
} else {
currentSeq = 1;
}
// maxSeq should be updated in time, otherwise it would be tossed away
maxSeq = std::max(maxSeq, currentSeq);
Log(i, currentSeq, maxSeq);
}
return maxSeq;
}
int main() {
int arr[] = {1, 2, 3, 4, 4, 4, 5, 7, 9, 10};
std::cout << LongestSequence(arr, sizeof(arr) / sizeof(arr[0])) << std::endl;
}
Output:
current[0] = 1, max[0] = 1
current[1] = 2, max[1] = 2
current[2] = 3, max[2] = 3
current[3] = 4, max[3] = 4
current[4] = 4, max[4] = 4
current[5] = 4, max[5] = 4
current[6] = 5, max[6] = 5
current[7] = 1, max[7] = 5
current[8] = 1, max[8] = 5
current[9] = 2, max[9] = 5
5
I am currently struggling with a homework problem for my Algorithms Class. A summary of the instruction:
The user enters an integer 'n' to determine the number of test cases.
The user individually enters another integer 'num' to determine the # of elements in each test case.
The user enters the elements of the individual array.
The algorithm has to process the array and determine whether it can be partitioned into two subsequences, each of which is in strictly increasing order. If the result is positive, the program prints "Yes", otherwise it prints "No".
I have 24 hours to complete this assignment but am struggling with the primary problem - I cannot properly process the user input. (come up with an algorithm to split the two subsequences)
update: I got to this solution. It passes 4/5 tests but fails the time constraint in the last test.
#include<iostream>
#include<string>
using namespace std;
bool run(){
int numbers;
int *arr;
cin >> numbers;
arr = new int[numbers];
for (int i = 0; i < numbers; i++)
cin >> arr[i];
long long int MAX = 0;
long long int MAX2 = 0;
string stra = "";
string strb = "";
string result = "";
string total = "";
long long int sum = 0;
for (int i = 0; i < numbers; i++){
if (arr[i] >= MAX && arr[i] != arr[i - 1]){
stra += to_string(arr[i]);
MAX = arr[i];
}
else
if (arr[i] >= MAX2 && MAX2 != MAX){
strb += to_string(arr[i]);
MAX2 = arr[i];
}
}
for (int i = 0; i < numbers; i++){
result = to_string(arr[i]);
total += result;
}
long long int len1 = stra.length();
long long int len2 = strb.length();
sum += len1 + len2;
delete[] arr;
if (sum != total.length())
return false;
else
return true;
}
int main()
{
int test;
cin >> test;
while (test > 0)
{
if (run())
cout << "Yes\n";
else
cout << "No\n";
test--;
}
system("pause");
}
Example input:
2
5
3 1 5 2 4
5
4 8 1 5 3
Example output:
Yes
No
Explanation: For the array 3 1 5 2 4, the two strictly increasing subsequences are: 3 5 and 1 2 4.
It seems that the existence of any equal or decreasing subsequence of at least three elements means the array cannot be partitioned into two subsequences, each with strictly increasing order, since once we've placed the first element in one part and the second element in the other part, we have no place to place the third.
This seems to indicate that finding the longest decreasing or equal subsequence is a sure solution. Since we only need one of length 3, we can record in O(n) for each element if it has a greater or equal element to the left. Then perform the reverse. If any element has both a greater or equal partner on the left and a smaller or equal partner on the right, the answer is "no."
We can visualise the O(n) time, O(1) space method by plotting along value and position:
A choosing list B here
A x would be wrong
x
value B z
^ B x
| x
| A
| x
|
| B
| x
- - - - - - - -> position
We notice that as soon as a second list is established (with the first decrease), any element higher than the absolute max so far must be assigned to the list that contains it, and any element lower than it can, in any case, only be placed in the second list if at all.
If we were to assign an element higher than the absolute max so far to the second list (that does not contain it), we could arbitrarily construct a false negative by making the next element lower than both the element we just inserted into the second list and the previous absolute max, but greater than the previous max of the second list (z in the diagram). If we had correctly inserted the element higher than the previous absolute max into that first list, we'd still have room to insert the new, arbitrary element into the second list.
(The JavaScript code below technically uses O(n) space in order to show the partition but notice that we only rely on the last element of each part.)
function f(A){
let partA = [A[0]];
let partB = [];
for (let i=1; i<A.length; i++){
if (A[i] > partA[partA.length-1])
partA.push(A[i]);
else if (partB.length && A[i] <= partB[partB.length-1])
return false;
else
partB.push(A[i]);
}
return [partA, partB];
}
let str = '';
let examples = [
[30, 10, 50, 25, 26],
[3, 1, 5, 2, 4],
[4, 8, 1, 5, 3],
[3, 1, 1, 2, 4],
[3, 4, 5, 1, 2],
[3, 4, 1],
[4, 1, 2, 7, 3]
];
for (e of examples)
str += JSON.stringify(e) + '\n' + JSON.stringify(f(e)) + '\n\n';
console.log(str);
I would go over the entire array once and check two maximal values. If the actual array value is smaller than both maxima, it is not possible, otherwise the proper maximum is increased.
The algorithm does not have to traverse the whole array, if the split condition is violated before.
Here is my code
#include <algorithm>
#include <iostream>
#include <vector>
bool isAddable(const int item, int &max1, int &max2) {
if (max2 > item) {
return false;
}
else {
if (max1 > item) {
max2 = item;
}
else {
max1 = item;
}
return true;
}
}
void setStartValue(int &max1, int &max2, const std::vector<int> &vec) {
max1 = *std::min_element(vec.begin(), vec.begin() + 3);
max2 = *std::max_element(vec.begin(), vec.begin() + 3);
}
bool isDiviableIntoTwoIncreasingArrays(const std::vector<int> &vec) {
if (vec.size() < 3) {
return true;
}
int max1, max2;
setStartValue(max1, max2, vec);
for (int i = 2; i < vec.size(); ++i) {
if (max1 > max2) {
if (!isAddable(vec[i], max1, max2)) {
return false;
}
}
else {
if (!isAddable(vec[i], max2, max1)) {
return false;
}
}
}
return true;
}
int main() {
std::vector<int> userVec;
int tmp1;
while (std::cin >> tmp1) {
userVec.emplace_back(tmp1);
}
const std::vector<int> v1{3, 1, 5, 2, 4};
const std::vector<int> v2{4, 8, 1, 5, 3};
const std::vector<int> v3{3, 4, 1};
for (const std::vector<int> &vec : {userVec, v1, v2, v3}) {
if (isDiviableIntoTwoIncreasingArrays(vec)) {
std::cout << "Yes\n";
}
else {
std::cout << "No\n";
}
}
}
I think you could resort to using a brute force solution. Notice here I use vectors(I think you should as well) to store the data and I use recursion to exhaust out all possible combinations. Keep the problem in mind, solve it and then focus on trivial tasks like parsing the input and matching the way your coursework expects you to enter data. I have added inline comments to make this understandable.
bool canPartition(vector<int>& nums) {
if(nums.empty()) return false;
vector<int> part1 = {}, part2 = {}; // two partitions
auto ans = canPart(nums, part1, part2, 0); // pass this to our recursive function
return ans;
}
bool canPart(vector<int>& nums, vector<int>& part1, vector<int>& part2, int i)
{
if(i >= nums.size()) // we are at the end of the array is this a solution?
{
if(!part1.empty() && !part2.empty()) // only if the partitions are not empty
{
//if you want you could print part1 and part2 here
//to see what the partition looks like
return true;
}
return false;
}
bool resp1empty = false, resp2empty = false, resp1 = false, resp2 = false;
if(part1.empty()) // first partition is empty? lets add something
{
part1.push_back(nums[i]);
resp1empty = canPart(nums, part1, part2, i + 1);
part1.pop_back(); // well we need to remove this element and try another one
}
else if(nums[i] > part1.back()) // first partition is not empty lets check if the sequence is increasing
{
part1.push_back(nums[i]);
resp1 = canPart(nums, part1, part2, i + 1);
part1.pop_back();
}
if(part2.empty()) // is partition two empty? lets add something
{
part2.push_back(nums[i]);
resp2empty = canPart(nums, part1, part2, i + 1);
part2.pop_back();
}
else if(nums[i] > part2.back()) // check if sequence is increasing
{
part2.push_back(nums[i]);
resp2 = canPart(nums, part1, part2, i + 1);
part2.pop_back();
}
//if any of the recursive paths returns a true we have an answer
return resp1empty || resp2empty || resp1 || resp2;
}
You can now try this out with a main function:
vector<int> v = {3,1,5,2,4};
cout << canPartition(v);
The key take away is make a small test case, add a few more non trivial test cases, solve the problem and then look into parsing inputs for other test cases
I think this comes down to whether you have an option for a number to appear in the first list or second list or not.
So, we will keep adding numbers to list 1 and if we can't add any element, we will make it as the start of the new list.
Let's say, we have both the lists going. If we come across an element to whom we can't add to any of the lists, we return false.
There does arise a situation where we could add an element to any of the 2 lists. In this scenario, we adopt a greedy approach as to add to which list.
We prepare an array of minimum values from the right. For example, for [30,10,50,25,26], we will have an array of minimums as [10,25,25,26,(empty here since last)].
Now, let's trace how we could divide them into 2 lists properly.
30 => List A.
10 => List B. (since you can't add it first list, so make a new one from here)
50 => List A.
Here, 50 applies to come after either 30 or 10. If we choose 10, then we won't be able to accommodate the next 25 in either of the 2 lists and our program would fail here itself, since our lists would look like [30] and [10,50]. However, we could continue further if we add 50 to 30 by checking for the minimum stored for it in our minimums array, which is 25.
25 => List B.
26 => List B.
So, our final lists are [30,50] and [10,25,26].
Time complexity: O(n), Space complexity: O(n) and you can print the 2 lists as well.
If we come across a sorted array which is strictly increasing, we return true for them anyway.
So I am trying to make a binary sort algorithm for my c++ class, but I keep getting a segmentation fault when my binarySearch function runs. No matter how hard me and my roommate look at it, we cant find the issue. Any help would be greatly appreciated.
int binarySearch(int arr[], int k, int first, int last)
{
if(arr[first] <= arr[last])
{
int mid = (first + last) / 2;
if(k == arr[mid])
{
return mid;
}
else if (k < arr[mid])
{
return binarySearch(arr, k, first, mid-1);
}
else return binarySearch(arr, k, mid+1, last);
}
else if(arr[first] >= arr[last])
{
int mid = (first + last) / 2;
if(k == arr[mid])
{
return mid;
}
else if (k < arr[mid])
{
return binarySearch(arr, k, mid+1, last);
}
else return binarySearch(arr, k, first, mid-1);
}
else return -1;
}
After fixing the segmentation fault, I noticed I must have an error somewhere in my logic because the program keeps outputting that the key was unable to be found even though it exists in the array.
Your code works actually, if the element you are searching for is in the array. However, it does not catch incorrect input.
When calling the function, make sure that:
first and last are between 0 and (array length - 1)
first < last
eg: if the array has 10 elements, first and last must be between 0 and 9.
Try this:
int main() {
int a[] = {134, 232, 233, 234, 587, 623, 793, 802, 963, 1074};
int b[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
int na = binarySearch(a, 587, 0, 9);
int nb = binarySearch(b, 3, 0, 9);
printf("na: %d\n", na); // prints 'na: 4'
printf("nb: %d\n", nb); // prints 'nb: 7'
}
If the element you are searching for is not in the array,
then the recursion does never terminate,
you can fix that by adding the following to the head of binarySearch():
if (first == last && k != arr[first])
return -1;
Not a major concern, but to prevent overflow it is usual to rewrite int mid = (first + last) / 2; as int mid = first + (last-first)>>1;
It also seems that you will never hit the line return -1 (the first two conditionals take care of all possible orderings)
An implementation (for strictly increasing, or decreasing array) could look like that
#include <cassert>
int binarySearch(int* array, int k, int l, int h, bool asc)
{
if (l>h)
return -1;
int m = l + ((h - l) >> 1);
if (array[m] == k)
return m;
else
{
if (array[m]>k)
return binarySearch(array, k, (asc ? l : m + 1), (asc ? m - 1 : h),asc);
else
return binarySearch(array, k, (asc ? m + 1 : l), (asc ? h : m - 1),asc);
}
}
int main()
{
int ascArray[7] = {1, 2, 3, 4, 5, 6, 7};
int descArray[7] = {7, 6, 5, 4, 3, 2, 1};
assert(binarySearch(ascArray, 7, 0, 6, ascArray[0] < ascArray[1]) == 6);
assert(binarySearch(descArray, 7, 0, 6, descArray[0] < descArray[1]) == 0);
}
instead of using if else if, you can try while loop:
int mid = (first + last)/2;
while(first<=last && arr[mid]!=k){
if(arr[mid]<k)
first=mid+1;
else
last=mid-1;
mid = (first + last)/2;
}
if(arr[mid] == k){
std::cout<<"Found"<<std::endl;
}else{
std::cout<<"Not Found"<<std::endl;
}
Instead you can use vector, that makes it very easy:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main(){
int arrint[] = {3,5,2,1,7,6,9,4,8,10};
vector<int> vector1(arrint,arrint+10);
sort(vector1.begin(),vector1.end());
int n;
cout<<"Enter element to search: ";
cin>>n;
cin.ignore();
cout<<endl;
if(binary_search(vector1.begin(),vector1.end(),n)){
cout<<"Found: "<<n<<endl;
}else{
cout<<n<<" Not Found"<<endl;
}
//cin.get();
return 0;
}
Make sure first>=last. I think the crash is because you don't find the element in the array.