sorted list of Doubly linked list. - doubly-linked-list

please help me with the insert method please. i want to insert an int which have a value greater than that int in the doubly linked list. then the new int will replace the greater value from the doubly linked list (i got that working fine). but now i want that greater int to go down the list and be inserted where appropriate.
if i say insert 2 4 6 8 , it will work (result: 2 4 6 8)
if i then insert 1 (which is smaller than 2)
result will be 1 4 6 8
2
the problem is, when i insert lets say 7 (greater than 6, but smaller than 8) i dont know how to go down row and insert where appropriate.
enter code here
public void insert(int newInt) {
Cell newCell = new Cell (newInt);
if (this.size() == 0){
smallest = newCell;
}
for (Cell i = smallest; i != null; i = i.right){
// if newCell is the larger than all in row, then add it to the end.
if (newCell.value > i.value && i.right == null){
i.right = newCell;
}
// if newCell is smaller than smallest, then add it to smallest and old smallest will go down the list (compare and put it where appropriate).
if (newCell.value < smallest.value){
newCell.below = smallest;
newCell.right = smallest.right;
smallest.right = null;
smallest = newCell;
}
// if newCell is smallest than any element, then replace that cell with newCell, and send that cell to below row
// do again untill all below rows are sorted. hence this could be a while loop.
if (newCell.value > i.value && newCell.value < i.right.value){
System.out.println("newCell is " + newCell);
Cell largerRight = i.right;
newCell.right = largerRight.right;
largerRight.right = null;
i.right = newCell;
temp = smallest.below;
while (temp != null){
System.out.println(temp);
for (Cell k = temp; k != null; k = k.right){
if (largerRight.value > temp.value && temp.right == null){
temp.right = largerRight;
temp = null;
}
}
temp =temp.below;
}
}
}
}//end insert

When inserting into a doubly linked list, you almost always have a first and last pointer. You also have a new node that you want inserted in the list, but as yet without the pointer information.
I'll do this in pseudo-code so you can translate it to whatever language you want (and since it may be homework).
First, detect an empty list. If that's the case, just build a new one-element list:
if first == NULL:
newNode->next = NULL # one element pointing nowhere.
newNode->prev = NULL
first = newNode # first and last are that element.
last = newNode
return
Otherwise, find the first node greater than the current, that's where you want to insert before:
insertPoint = first # scan list until end.
while insertPoint != NULL:
if insertPoint->payload > newNode->payload: # stop if found one greater.
break;
insertPoint = insertPoint->next # move to next.
Now, there are three distinct possibilities here:
insertPoint is the first node, meaning you have to insert at the front;
insertPoint is NULL, meaning you have to append to the list; or
insertPoint is a valid node other than the first.
First, handle the two special cases. These are considered special since they change the control information of the list (first and last), rather than just pointers within the nodes themselves:
if insertPoint == first: # inserting at front.
newNode->next = first # new node points forwards to current first
newNode->prev = NULL # and backwards to nowhere.
first = newNode # and update first.
return
if insertPoint = NULL: # appending to end.
newNode->next = NULL # new node points forwards to nowhere
newNode->prev = last # and backwards to current last.
last = newNode # and update last.
return
If you've gotten to here, you simply need to insert, knowing that you have a valid current and previous node:
newNode->next = insertPoint # set new node to point to correct nodes.
newNode->prev = insertPoint->prev
insertPoint->prev->next = newNode # update those nodes to point to new node.
insertPoint->prev = newNode
return

Related

GEN_NO_GENERATABLE_NOTIF

I want to choose index from list, so the element[index] complies my condition.
MyList[index].num==0
I tried the code bellow:
gen DescIdx2Choose keeping {
it < MyList.size();
MyList[it].num==0;//I tried a few way of read_only
};
How can I do it without using all_indices?
Thanks
Since you generating DescIdx2Choose then MyList will be input to the problem.
Therefore,
If seeking for the first index (if exists) then using random generation isn't required. Use the procedural code "first_index" as user3467290 suggested which is much more efficient:
var fidx := MyList.first_index(.num == 0);
if ( fidx != UNDEF ) {
DescIdx2Choose = fidx;
} else {
// error handling
};
If there are multiple indices and it is required to choose a random one, the most efficient way would be using "all_indices" as Thorsten suggested:
gen DescIdx2Choose keeping {
it in read_only( MyList.all_indices(.num == 0) );
};
The reason is the random generator doesn't need to read all possible values of "MyList.num" only a shorter list of valid indices.
This should do it, but MyList must fulfill the condition otherwise you get a contradiction. Pseudo-method first_index() is not bi-directional which is what we need here.
gen DescIdx2Choose keeping {
MyList.first_index(.num == 0) == it;
};
Maybe i missed something in the question, but If you always want the index of the first element that its num == 0, then why use constraints? can assign DescIdx2Choose == MyList.first_index(.num == 0).
To ensure that there is at least one such element, can constrain MyList.has(.num == 0).
Do you have additional constraints on DescIdx2Choose ?

Is there a way to only keep the last two saved values of the recursion in a dictionary and delete the rest?

i am using memoization to save the last calculated values of fibonacci numbers in a dictionary. Since (i suppose) that we don't need all of the values that were previously calculated in our dictionary so, i want to delete them. specifically i only want to keep only the last two calculated fibonacci numbers, is there a way to do it?
import sys
sys.setrecursionlimit(10000)
cache = {}
def fib(n):
if n in cache:
return cache[n]
elif n <= 2:
value = 1
else:
value = fib(n-1) + fib(n-2)
cache[n] = value
return value
print(fib(1000))
ok i found it.
cache = {}
for x in range(1, 1000001):
if x > 4:
cache.pop(x-3, x-4)
if x <= 2:
value = 1
cache[x] = value
else:
value = cache[x - 1] + cache[x - 2]
cache[x] = value
print(value)

Script is taking 11 - 20 seconds to lookup up an item in an 18,000 row data set

I have two Google sheets workbooks.
One is the "master" source of lookup data with a key based on manufacturer item #, which could be anything from 1234 to A-01/234-Name_1. This sheet, referenced via SpreadsheetApp.openByUrl, has 18,000 rows and 13 columns. The key column has been converted to plain text and the sheet is sorted by this column.
The second is the "template" where people enter item #s that they need to look up against the master, typically 20 - 1500 items at a time.
The script is in the template. It is very slow and routinely times out after 30 minutes. It was written by someone else and I am new to App Script, but I think I've managed to understand what the script is doing and where the bottleneck is occurring.
It does a bunch of stuff, but this is the meat of the lookup:
var numrows = master.getDataRange().getNumRows();
var masterdata = master.getDataRange().getValues();
var itemnumberlist = template.getDataRange().getValues();
var retreiveddata = [];
// iterate through the manf item number list to find all matches in the
// master and return those matches to another sheet
for (i = 1; i < template.getDataRange().getValues().length; i++) {
for (j = 0; j < numrows; j++) {
if (masterdata[j][1].toString() === itemnumberlist[i][1].toString()) {
retreiveddata.push(data[j]);
anothersheet.appendRow(data[j]);
}
}
}
I used Logger.log() to determine that each time through the i loop is taking 11 - 19 seconds, which just seems insane.
I've been doing some google searching and I've tried a couple of different things...
First I tried moving the writing of found data out of the for loop so the script would be doing all of its reading first and then writing in one big chunk, but I couldn't get it exactly right. My two attempts are below.
var mycounter = 0;
for (i = 0; i < template.getDataRange().getValues().length; i++) {
for (j = 0; j < numrows; j++) {
if (masterdata[j][0].toString() === itemnumberlist[i][0].toString()) {
retreiveddata.push(masterdata[j]);
mycounter = mycounter + 1;
}
}
}
// Attempt 1
// var myrange = retreiveddata.length;
// for(k = 0; k < myrange; k++) {
// anothersheet.appendRow(retreiveddata.pop([k]);
// }
//Attempt 2
var myotherrange = anothersheet.getRange(2,1,myothercounter, 13)
myotherrange.setValues(retreiveddata);
I can't remember for sure, because this was on Friday, but I think both attempts resulted in the script trying to write the entire master file into "anothersheet".
So I temporarily set this aside and decided to try something else. I was trying to recreate the issue in a couple of sample spreadsheets, but I was unable to do so. The same script is getting through my 15,000 row sample "master" file in less than 1 second per lookup. The only thing I can think of is that I used a random number as my key instead of a weird text string.
That led me to think that maybe I could use a hash algorithm on both the master data and the values to be looked up, but this is presenting a whole other set of issues.
I borrowed these functions from another forum post:
function GetMD5Hash(value) {
var rawHash = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5,
value);
var txtHash = '';
for (j = 0; j <rawHash.length; j++) {
var hashVal = rawHash[j];
if (hashVal < 0)
hashVal += 256;
if (hashVal.toString(16).length == 1)
txtHash += "0";
txtHash += hashVal.toString(16);
Utilities.sleep(100);
}
return txtHash;
}
function RangeGetMD5Hash(input) {
if (input.map) { // Test whether input is an array.
return input.map(GetMD5Hash); // Recurse over array if so.
Utilities.sleep(100);
} else {
return GetMD5Hash(input)
}
}
It literally took me all day to get the hash value for all 18,000 item #s in my master spreadsheet. Neither GetMD5Hash nor RangeGetMD5Hash will return a value consistently. I can only do a few rows at a time. Sometimes I get "Loading..." indefinitely. Sometimes I get "#Name" with a message about GetMD5Hash being undefined (despite the fact that it worked on the previous row). And sometimes I get "#Error" with a message about an internal error.
This method actually reduces the lookup time of each item to 2 - 3 seconds (much better, but not great). However, I can't get the hash function to consistently work on the input data.
At this point I'm so frustrated and behind on my other work that I thought I'd reach out to the smart people on these forums and hope for some sort of miracle response.
To summarize, I'm looking for suggestions on these three items:
What am I doing wrong in my attempt to move the write out of the for loop?
Is there a way to get my hash value faster or utilize a different method to accomplish the same goal?
What else can I try to help speed up the script?
Any suggestions you can offer would be greatly appreciated!
-Mandy
It sounds like you hit on the right approach with attempting to move the appendRow() call out of the loop. Anytime you are reading or writing to a spreadsheet you can expect the individual call to take 1 to 2 seconds, so this will eat up a lot of time when you get matches. Storing the matches in an array and writing them all at once is the way to go.
Another thing I notice is that your script calls getValues() in the actual for loop condition statement. The condition statement is executed each time on each iteration of the loop, so this is potentially wasting a lot of time even when you don't have matches.
A final tweak that may be helpful depending on your desired behaviour. You can stop the inner for loop after it finds the first match, which, if you only care about the first match or know there will only be one match, will save you a lot of iterations. To do this, put "break" immediately after the retreiveddata.push(masterdata[j]); line.
To fix the getValues issue, Change:
for (i = 1; i < template.getDataRange().getValues().length; i++) {
To:
for (i = 1; i < itemnumberlist.length; i++) {
And that fix along with the appendRow issue, and including the break call:
for (i = 1; i < itemnumberlist.length; i++) {
for (j = 0; j < numrows; j++) {
if (masterdata[j][0].toString() === itemnumberlist[i][0].toString()) {
retreiveddata.push(masterdata[j]);
break; //stop searching after first match, move on to next item
}
}
}
//make sure you have data to write before trying to write it.
if(retreiveddata.length > 0){
var myotherrange = anothersheet.getRange(2,1,retreiveddata.length, retreiveddata[0].length);
myotherrange.setValues(retreiveddata);
}
If you are re-using the same sheet for "anothersheet" on each execution, you may also want to call anothersheet.clear() to erase any existing data before you write your fresh results.
I would pass on the hashing approach altogether, comparing strings is comparing strings, so whether they are hashes or actual part numbers I wouldn't expect a significant difference.

Merging to dataset together according to key

I have two datasets stored in a cell array and a double array, respectively. The design of the two arrays is:
Array 1 (name: res) (double) is composed of two columns; a unique id column and a data column.
Array 2 (name: config) (cell array) contains 3 column cells, each with a string inside. The last cell in the array contains a id double integer matching the id's in Array 1. The double integer in the cell array is converted to a double when necessary.
I want to merge the two datasets in order to have the 3 cells in the cell array AND the result column in Array 1 in one common cell array. How do I do this?
I have the following code. The code does not return the correct order of the results.
function resMat = buildResultMatrix(res, config)
resMat = {};
count = 1;
count_max = size(res,1)/130;
for i = 1 : size(res,1)
for j = 1 : size(res,1)
if isequal(res(i),str2double(config{j,3}))
if i == 1
resMat(end+1,:) = {config{j,:} res(j,2:end)};
else
if count == 1
resMat(end+1,:) = {config{j,:} res(j,2:end)};
elseif count == count_max
resMat(end+1,:) = {config{j,:} res(j,2:end)};
else
resMat(end+1,:) = {config{j,:} res(j,2:end)};
end
count = count + 1;
end
end
end
count = 1;
end
end
First convert the id in config to numbers:
config(:,3) = num2cell(str2double(config(:,3)));
Then run this:
res = sortrows(res,1);
config(:,4) = num2cell(res(cell2mat(config(:,3)),2))
this will put the data from res in the 4th column in config in the row with the same id.

How to automatically generate sequent numbers when using a form

Ahab stated in 2010: the complex looking number based on the Timestamp has one important property, the number can not change when rows are deleted or inserted.
As long as the submitted data is not changed by inserting deleting rows the simple formula =ArrayFormula(ROW(A2:A) - 1) may be the easiest one to use.
For other situations there is no nice reliable solution. :(
Now we live in 2015. Maybe times have changed?
I need a reliable way to number entries using a form.
Maybe a script can do the trick? A script that can add 1 to each entry?
That certain entry has to keep that number even when rows are deleted or inserted.
I created this simple spreadsheet in which I added 1,2, and 3 manually,please have a look:
https://docs.google.com/spreadsheets/d/1H9EXns8-7m9oLbCrTyIZhLKXk6TGxzWlO9pOvQSODYs/edit?usp=sharing
The script has to find the maximum of the former entries, which is 3, and then add 1 automatically.
Who can help me with this?
Grtz, Bij
Maybe a script can do the trick? A script that can add 1 to each
entry?
Yes, that would be what you need to resort to. I took the liberty of entering this in your example ss:
function onEdit(e) {
var watchColumns = [1, 2]; //when text is entered in any of these columns, auto-numbering will be triggered
var autoColumn = 3;
var headerRows = 1;
var watchSheet = "Form";
var range = e.range;
var sheet = range.getSheet();
if (e.value !== undefined && sheet.getName() == watchSheet) {
if (watchColumns.indexOf(range.getColumn()) > -1) {
var row = range.getRow();
if (row > headerRows) {
var autoCell = sheet.getRange(row, autoColumn);
if (!autoCell.getValue()) {
var data = sheet.getDataRange().getValues();
var temp = 1;
for (var i = headerRows, length = data.length; i < length; i++)
if (data[i][autoColumn - 1] > temp)
temp = data[i][autoColumn - 1];
autoCell.setValue(temp + 1);
}
}
}
}
}
For me the best way is to create a query in a second sheet pulling everything from form responses in to second column and so on. then use the first column for numbering.
In your second sheet B1 you would use:
=QUERY(Form!1:1004)
In your second sheet A2 you would use:
=ARRAYFORMULA(if(B2:B="",,Row(B2:B)-1))
I made a second sheet in your example spreadsheet, have a look at it.