Optimize a list of text additions and deletions - algorithm

I've got a list containing positions of text additions and deletions, like this:
Type Position Text/Length
1. + 2 ab // 'ab' was added at position 2
2. + 1 cde // 'cde' was added at position 1
3. - 4 1 // a character was deleted at position 4
To make it more clear, this is what these operations will do:
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
---------------------------------
t | e | x | t | | | | |
1. t | a | b | e | x | t | | |
2. c | d | e | t | a | b | e | x | t
3. c | d | e | a | b | e | x | t |
The number of actions can be reduced to:
Type Position Text/Length
1. - 1 1 // 't' was deleted at position 1
2. + 1 cdeab // 'cdeab' was added at position 1
Or:
Type Position Text/Length
1. + 1 cdeab // 'cdeab' was added at position 1
2. - 6 1 // 't' was deleted at position 6
These actions are to be saved in my database and in order to optimize this: how can I reduce the number of actions that are to be done to get the same result? Is there a faster way than O(n*n)?
Note that these actions are chronological, changing the order of the actions will give another result.

Not a solution, just some thoughts:
Rule 1: if two consecutive operations don't have overlapping ranges, they can be swapped (with positions adjusted)
Rule 2: two consecutive inserts or removals at the same position can be joined
Rule 3: when an insert is followed by a removal that is completely contained in the insert, they can be joined
I don't see a straightforward algorithm for the shortest solution. However, an heuristic approach using Rule 1 + 2 might be:
move operations "up" unless
you'd violate Rule 1
you'd move an insert before a removal
the position is less than that of that predecessor
join consecutive inserts/removals at the same position
Applied to the sample, this would mean:
+ 2 ab
+ 1 cde
- 4 1
Rule 1 (2x):
+ 2 ab
- 1 1 // position adjusted by -3
+ 1 cde
.
- 1 1
+ 1 ab // position adjusted
+ 1 cde
Rule 2:
- 1 1
+ 1 cdeab // watch correct order!
A primitive implementation will be O(N*N) - basically, a bubble sort with additonal stopping conditions. I'm not sure about beating down that complexity, since standard algorithms are of no use here due to having to adjust the position.
However, you might be able to improve things notably - e.g. you don't need a "full sort"

Make a binary tree representing the document before and after applying all the changes. Each node represents either original text or inserted/deleted text; the latter kind of node includes both the amount of original text to delete (possibly 0) and the string of text to insert (possibly empty).
Initially the tree has just one node, "0 to end: original text". Apply all the changes to it merging changes as you go wherever possible. Then walk the tree from beginning to end emitting the final set of edits. This is guaranteed to produce the optimal result.
Applying an insert: Find the appropriate point in the tree. If it's in the middle of or adjacent to inserted text, just change that node's text-to-insert string. Otherwise add a node.
Applying a delete: Find the starting and ending points in the tree—unlike an insert, a delete may cover a whole range of existing nodes. Modify the starting and ending nodes accordingly, and kill all the nodes in between. After you're done, check to see if you have adjacent "inserted/deleted text" nodes. If so, join them.
The only tricky bit is making sure you can find points in the tree, without updating the entire tree every time you make a change. This is done by caching, at each node, the total amount of text represented by that subtree. Then when you make a change, you only have to update these cached values on nodes directly above the nodes you changed.
This looks strictly O(n log n) to me for all input, if you bother to implement a balanced tree and use ropes for the inserted text. If you ditch the whole tree idea and use vectors and strings, it's O(n2) but might work fine in practice.
Worked example. Here is how this algorithm would apply to your example, step by step. Instead of doing complicated ascii art, I'll turn the tree on its side, show the nodes in order, and show the tree structure by indentation. I hope it's clear.
Initial state:
*: orig
I said above we would cache the amount of text in each subtree. Here I just put a * for the number of bytes because this node contains the whole document, and we don't know how long that is. You could use any large-enough number, say 0x4000000000000000L.
After inserting "ab" at position 2:
2: orig, 2 bytes
*: insert "ab", delete nothing
*: orig, all the rest
After inserting "cde" at position 1:
1: orig, 1 byte
5: insert "cde", delete nothing
1: orig, 1 byte
*: insert "ab", delete nothing
*: orig, all the rest
The next step is to delete a character at position 4. Pause here to see how we find position 4 in the tree.
Start at the root. Look at the first child node: that subtree contains 5 characters. So position 4 must be in there. Move to that node. Look at its first child node. This time it contains only 1 character. Not in there. This edit contains 3 characters, so it's not in here either; it's immediately after. Move to the second child node. (This algorithm is about 12 lines of code.)
After deleting 1 character at position 4, you get this...
4: orig, 1 byte
3: insert "cde", delete nothing
*: insert "ab", delete nothing
*: orig, all the rest
...and then, noticing two adjacent insert nodes, you merge them. (Note that given two adjacent nodes, one is always somewhere above the other in the tree hierarchy. Merge the data into that higher node; then delete the lower one and update the cached subtree sizes in between.)
1: orig, 1 byte
*: insert "cdeab", delete nothing
*: orig, all the rest

The "diff" tools used in source code control systems use algorithms that produce the minimum edit needed to transform one piece of source code to another - it might be worth investigating them. I think most of them are based (eventually) on this algorithm, but it's a while since I did any reading on this subject.

I believe that this can be done considerably faster than O(n²) on average (it is likely that input can be engineered not to allow fast analysis). You can regard consecutive additions or deletions as sets. You can analyze one operation at a time, and you will have to do some conditional transformations:
If an addition follows an addition, or a set of additions, it might
touch (one or more of) the previous addition(s): then, you can unite these additions
not touch: you can order them (you will have to adjust the positions)
If a deletion follows an addition, or a set of additions, it might
only delete characters from the addition: then, you can modify the addition (unless it would split an addition)
only delete characters not from the set of additions: then, you can move the deletion to a position before the set of additions, and perhaps unite additions; after that, the set of deletions before the current set of additions might have to be applied to the additions before that
do both: then, you can first split it into two (or more) deletions and apply the respective method
If a deletion follows a deletion, or a set of deletions, it can:
touch (one or more of) the previous deletion(s): then, you can unite these deletions
not touch: you can order them (you will have to adjust the positions
in any case, you then have to apply analysis of the newly formed deletions on the previous additions
If an addition follows a deletion, no transformation is needed at this point
This is just a first rough draft. Some things may have to be done differently, e.g., it might be easier or more efficient to always apply all deletions, so that the result is always only one set of deletions followed by one set of additions.

Let's assume for simplicity that only letters a-z appear in your texts.
Initialize a list A with values a[i] = i for i = 1 to N (you will figure out yourself how big N should be).
Perform (simulate) all your operations on A. After this analyze A to find required operations:
Fist find required delete operations by finding missing numbers in A (they will form groups of consecutive values, one group stands for one delete operation).
After this you can find required insert operations by finding sequences of consecutive
letters (one sequence is one insert operation).
In your example:
init A:
1 2 3 4 5 6 7 8 9 10
Step 1 (+:2:ab):
1 a b 2 3 4 5 6 7 8 9 10
Step2 (+:1:cde):
c d e 1 a b 2 3 4 5 6 7 8 9 10
Step3 (-:4:1):
c d e a b 2 3 4 5 6 7 8 9 10
Now we search for missing numbers to find deletes. In our example only one number (namely number 1) is missing,
so only 1 delete is required, so we have one delete operation:
-:1:1
(In general there may be more numbers missing, every sequence of missing numbers is one delete operation.
For example if 1, 2, 3, 5, 6, 10 are all missing numbers, then there are 3 delete operations: -:1:3, -:2:2, -:5:1. Remember that after every delete operation all indexes are decreased, you have to store total sum of former delete operations to calculate the index of current delete operation.)
Now we search for character sequences to find insert operations. In our example there is only one sequence:
cdeab at index 1, so we have one insert operation: +:1:cdeab
Hope this is clear enough.

How to reduce the number of actions: An algorithmic approach could try to sort the actions. I think, that after sorting:
The chance that neighbouring actions can be joined (in the manner Svante and peterchen showed),
will rise.
This may lead to the minimum number of actions that have to be performed?
In the following "position-number" stands for the text insertion or deletion position.
Assuming it is possible to swap two neighboring actions (by adjusting position-numbers and
text/length property of this two actions), we can bring the action-list to any order we
like. I suggest to bring the deletion actions to the front of the action list with ascending
position-numbers. After the deletion actions the addition-actions are sorted with ascending
position-numbers.
The following examples should demonstrate, why i think it is possible to swap any neighboring actions.
Swaping following actions:
1. + 2 aaa -> taaaext
2. - 3 1 -> taaext
yields to one action:
1. + 2 aa -> taaext
Swaping following actions:
1. + 3 aaa -> teaaaxt
2. + 1 bb -> bbteaaaxt
yields to:
1. + 1 bb -> bbtext
2. + 5 aaa -> bbteaaaxt
Swaping following actions:
1. + 1 bb -> bbtext
2. - 2 2 -> bext
yields to:
1. - 1 1 -> ext
2. + 1 b -> bext
As the first example shows, in some cases a swap causes the removal of a deletion. This is a
benefiting side effect. This is also the matter why i suggest to move all deletions to the
front.
I hope that i didn't forget something and considered all circumstances.

Related

How to display all ways to give change

As far as I know, counting every way to give change to a set sum and a starting till configuration is a classic Dynamic Programming problem.
I was wondering if there was a way to also display (or store) the actual change structures that could possibly amount to the given sum while preserving the DP complexity.
I have never saw this issue being discussed and I would like some pointers or a brief explanation of how this can be done or why this cannot be done.
DP for change problem has time complexity O(Sum * ValuesCount) and storage complexity O(Sum).
You can prepare extra data for this problem in the same time as DP for change, but you need more storage O(O(Sum*ValuesCount), and a lot of time for output of all variants O(ChangeWaysCount).
To prepare data for way recovery, make the second array B of arrays (or lists). When you incrementing count array A element from some previous element, add used value to corresponding element of B. At the end, unwind all the ways from the last element.
Example: values 1,2,3, sum 4
index 0 1 2 3 4
A 0 1 2 3 4
B - 1 1 2 1 2 3 1 2 3
We start unwinding from B[4] elements:
1-1-1-1 (B[4]-B[3]-B[2]-B[1])
2-1-1 (B[4]-B[2]-B[1])
2-2 (B[4]-B[2])
3-1 (B[4]-B[1])
Note that I have used only ways with non-increasing values to avoid permutation variants (i.e. 1-3 and 3-1)

Sorting second column relative to first column

I have got the following sequence (representing a tree):
4 2
1 4
3 4
5 4
2 7
0 7
6 0
Now, I am trying to sort this sequence, such that when a value appears on the left (column 1), it has already appeared on the right (column 2). More concretely, the result of the sorting algorithm should be:
1 4
3 4
5 4
4 2
2 7
6 0
0 7
Obviously, this works in O(n^2) with an algorithm iterating over each entry of column 1 and then look for corresponding entries in column two. But as n can be quite big (> 100000) in my scenario, I'm looking for a O(n log n) way to do it. Is this even possible?
Assumption:
I'm assuming this is also a valid sort sequence:
1 4
4 2
3 4
5 4
2 7
6 0
0 7
i.e. Once a value appears once on the right, it can appear on the left.
If this is not the case (i.e. all occurrences on the right has to be before any occurrence on the left), ignore the "remove all edges pointing to that element" part and only remove the intermediate element if it has no incoming edges left.
Algorithm:
Construct a graph where each element A points to another element B if the right element of A is equal to the left element of B. This can be done using a hash multi-map:
Go through the elements, inserting each element A into the hash map as A.left -> A.
Go through the elements again, connecting each element B with all elements appearing under B.right.
Perform a topological sort of the graph, giving you your result. I should be modified such that, instead of removing an edge pointing to an element, we remove all edges pointing to that element (i.e. if we already found an element containing some element on the right, we don't need to find another for that element to appear on the left).
Currently this is O(n2) running time, because there are too many edges - if we have:
(1,2),(1,2),...,(1,2),(2,3),(2,3),...,(2,3)
There are O(n2) edges.
This can be avoided by, instead of having elements point directly to each other, create an intermediate element. In the above case, 1/2 the elements will point to that element and that element will point to the other half. Then, when doing the topological sort, when we would've remove an edge to that element, we instead remove that element and all edges pointing from / to it.
Now there will be a maximum of O(n) edges, and, since topological sort can be done in linear time with respect to the elements and edges, the overall running time is O(n).
Note that it's not always possible to get a result: (1,2), (2,1).
Illustrations:
For your example (pre-optimization), we'd have:
For my example above, we'd have:

What data structure would be best to check if value is between a and b defining block boundaries

I have the following table:
block | start | end
1 | 1 | 4
2 | 5 | 9
3 | 10 | 20
4 | 21 | 50
..........
n | 1000 | 2000
When given a value to variable c i have to search which block contains c ( start < c < end ). For example when c = 1001, c is in block n.
What data structure would be the most efficient?
If the entire range of your integers is only 1..2000 you could think of your data as a table like this:
n block
1 1
2 1
3 1
4 0
5 0
6 2
.
.
.
which you'd implement as a vector of 2000 elements. Simple look up of element n in the vector will tell you which block n is in (unless you've chosen to implement in a language with 0-based indices in which case element n tells you which block integer n+1 is in). Here I've used 0 to indicate 'no block'.
This trades space for time compared with some of the other answers here, but that's often an acceptable trade off.
An interval tree would be fairly appropriate for this problem, I think. It's certainly a lot harder to implement than a table, and if you don't need to dynamically add your blocks, I would suggest just keeping with a table and using a simple binary search for finding your blocks. That should achieve the same efficiency as the interval tree without all the terrible coding pains (as long as you don't have to add intervals).
An interval tree or segment tree would work. Essentially, you'd be able to binary search through the intervals to find which contains the point in question. Since Segment Tree's are better at stabbing queries, it'd be the first I'd try.
If you are using Java, I've implemented them both in java before. You can find the interval tree here and the segment tree here.
I'm not sure what you are asking for
I believe there are many ways of implementing such blocks,
personnaly I would have an ordrer list containing the end of each block and run through it until the end is greater than the value, the index giving me the number of block
For this particular example you can store start in an array (which is in sorted order) and do binary search to get to the appropriate block.
Edit explaining the example:
In the start array you have 1,5,10,21,1000 so for c=3 you know it is in block 1 because 5 is the start index of block 2.
For some reason if you also want to look at end then you can store that in another array and access end of a start from the index that you got from binary search.

calendar scheduler algorithm

I'm looking for an algorithm that, given a set of items containing a start time, end time, type, and id, it will return a set of all sets of items that fit together (no overlapping times and all types are represented in the set).
S = [("8:00AM", "9:00AM", "Breakfast With Mindy", 234),
("11:40AM", "12:40PM", "Go to Gym", 219),
("12:00PM", "1:00PM", "Lunch With Steve", 079),
("12:40PM", "1:20PM", "Lunch With Steve", 189)]
Algorithm(S) => [[("8:00AM", "9:00AM", "Breakfast With Mindy", 234),
("11:40AM", "12:40PM", "Go to Gym", 219),
("12:40PM", "1:20PM", "Lunch With Steve", 189)]]
Thanks!
This can be solved using graph theory. I would create an array, which contains the items sorted by start time and end time for equal start times: (added some more items to the example):
no.: id: [ start - end ] type
---------------------------------------------------------
0: 234: [08:00AM - 09:00AM] Breakfast With Mindy
1: 400: [09:00AM - 07:00PM] Check out stackoverflow.com
2: 219: [11:40AM - 12:40PM] Go to Gym
3: 79: [12:00PM - 01:00PM] Lunch With Steve
4: 189: [12:40PM - 01:20PM] Lunch With Steve
5: 270: [01:00PM - 05:00PM] Go to Tennis
6: 300: [06:40PM - 07:20PM] Dinner With Family
7: 250: [07:20PM - 08:00PM] Check out stackoverflow.com
After that i would create a list with the array no. of the least item that could be the possible next item. If there isn't a next item, -1 is added:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
1 | 7 | 4 | 5 | 6 | 6 | 7 | -1
With that list it is possible to generate a directed acyclic graph. Every vertice has a connection to the vertices starting from the next item. But for vertices where already is a vertices bewteen them no edge is made. I'll try to explain with the example. For the vertice 0 the next item is 1. So a edge is made 0 -> 1. The next item from 1 is 7, that means the range for the vertices which are connected from vertice 0 is now from 1 to (7-1). Because vertice 2 is in the range of 1 to 6, another edge 0 -> 2 is made and the range updates to 1 to (4-1) (because 4 is the next item of 2). Because vertice 3 is in the range of 1 to 3 one more edge 0 -> 3 is made. That was the last edge for vertice 0. That has to be continued with all vertices leading to such a graph:
Until now we are in O(n2). After that all paths can be found using a depth first search-like algorithm and then eliminating the duplicated types from each path.
For that example there are 4 solutions, but none of them has all types because it is not possible for the example to do Go to Gym, Lunch With Steve and Go to Tennis.
Also this search for all paths has a worst case complexity of O(2n). For example the following graph has 2n/2 possible paths from a start vertice to an end vertice.
(source: archive.org)
There could be made some more optimisation, like merging some vertices before searching for all paths. But that is not ever possible. In the first example vertice 3 and 4 can't be merged even though they are of the same type. But in the last example vertice 4 and 5 can be merged if they are of the same type. Which means it doesn't matter which activity you choose, both are valid. This can speed up calculation of all paths dramatically.
Maybe there is also a clever way to consider duplicate types earlier to eliminate them, but worst case is still O(2n) if you want all possible paths.
EDIT1:
It is possible to determine if there are sets that contain all types and get a t least one such solution in polynomial time. I found a algorithm with a worst case time of O(n4) and O(n2) space. I'll take an new example which has a solution with all types, but is more complex.
no.: id: [ start - end ] type
---------------------------------------------------------
0: 234: [08:00AM - 09:00AM] A
1: 400: [10:00AM - 11:00AM] B
2: 219: [10:20AM - 11:20AM] C
3: 79: [10:40AM - 11:40AM] D
4: 189: [11:30AM - 12:30PM] D
5: 270: [12:00PM - 06:00PM] B
6: 300: [02:00PM - 03:00PM] E
7: 250: [02:20PM - 03:20PM] B
8: 325: [02:40PM - 03:40PM] F
9: 150: [03:30PM - 04:30PM] F
10: 175: [05:40PM - 06:40PM] E
11: 275: [07:00PM - 08:00PM] G
1.) Count the different types in the item set. This is possible in O(nlogn). It is 7 for that example.
2.) Create a n*n-matrix, that represents which nodes can reach the actual node and which can be reached from the actual node. For example if position (2,4) is set to 1, means that there is a path from node 2 to node 4 in the graph and (4,2) is set to 1 too, because node 4 can be reached from node 2. This is possible in O(n2). For the example the matrix would look like that:
111111111111
110011111111
101011111111
100101111111
111010111111
111101000001
111110100111
111110010111
111110001011
111110110111
111110111111
111111111111
3.) Now we have in every row, which nodes can be reached. We can also mark each node in a row which is not yet marked, if it is of the same type as a node that can be reached. We set that matrix positions from 0 to 2. This is possible in O(n3). In the example there is no way from node 1 to node 3, but node 4 has the same type D as node 3 and there is a path from node 1 to node 4. So we get this matrix:
111111111111
110211111111
121211111111
120121111111
111212111111
111121020001
111112122111
111112212111
111112221211
111112112111
111112111111
111111111111
4.) The nodes that still contains 0's (in the corresponding rows) can't be part of the solution and we can remove them from the graph. If there were at least one node to remove we start again in step 2.) with the smaller graph. Because we removed at least one node, we have to go back to step 2.) at most n times, but most often this will only happend few times. If there are no 0's left in the matrix we can continue with step 5.). This is possible in O(n2). For the example it is not possible to build a path with node 1 that also contains a node with type C. Therefore it contains a 0 and is removed like node 3 and node 5. In the next loop with the smaller graph node 6 and node 8 will be removed.
5.) Count the different types in the remainig set of items/nodes. If it is smaller than the first count there is no solution that can represent all types. So we have to find another way to get a good solution. If it is the same as the first count we now have a smaller graph which still holds all the possible solutions. O(nlogn)
6.) To get one solution we pick a start node (it doesn't matter which, because all nodes that are left in the graph are part of a solution). O(1)
7.) We remove every node that can't be reached from the choosen node. O(n)
8.) We create a matrix like in step 2.) and 3.) for that graph and remove the nodes that can not reach nodes of any type like in step 4.). O(n3)
9.) We choose one of the next nodes from the node we choosen before and continue with 7.) until there we are at a end node and the graph only has one path left.
That way it is also possible to get all paths, but that can still be exponential many. After all it should be faster than finding solutions in the original graph.
Hmmm, this reminds me of a task in the university, I'll describe what i can remember
The run-time is O(n*logn) which is pretty good.
This is a greedy approuch..
i will refine your request abit, tell me if i'm wrong..
Algorithem should return the MAX subset of non colliding tasks(in terms of total length? or amount of activities? i guess total length)
I would first order the list by the finishing times(first-minimum finishing time,last-maximum) = O(nlogn)
Find_set(A):
G<-Empty set;
S<-A
f<-0
while S!='Empty set' do
i<-index of activity with earliest finish time(**O(1)**)
if S(i).finish_time>=f
G.insert(S(i)) \\add this to result set
f=S(i).finish_time
S.removeAt(i) \\remove the activity from the original set
od
return G
Run time analysis:
initial ordering :nlogn
each iteration O(1)*n = O(n)
Total O(nlogn)+O(n) ~ O(nlogn) (well, given the O notation weakness to represent real complexety on small numbers.. but as the scale grow, this is a good algo)
Enjoy.
Update:
Ok, it seems like i've misread the post, you can alternatively use dynamic programming to reduce running time, there is a solution in link text page 7-19.
you need to tweak the algorithm a bit, first you should build the table, then you can get all variations on it fairly easy.
I would use an Interval Tree for this.
After you build the data structure, you can iterate each event and perform an intersection query. If no intersections are found, it is added to your schedule.
Yes exhaustive search might be an option:
initialise partial schedules with earliest tasks that overlap (eg 9-9.30
and 9.15-9.45)
foreach partial schedule generated so far generate a list of new partial schedules appending to each partial schedule the earliest task that don't overlap (generate more than one in case of ties)
recur with new partial schedules
In your case initlialisation would produce only (8-9 breakfast)
After the first iteration: (8-9 brekkie, 11.40-12.40 gym) (no ties)
After the second iteration: (8-9 brekkie, 11.40-12.40 gym, 12.40-1.20 lunch) (no ties again)
This is a tree search, but it's greedy. It leaves out possibilities like skipping the gym and going to an early lunch.
Since you're looking for every possible schedule, I think the best solution you will find will be a simple exhaustive search.
The only thing I can say algorithmically is that your data structure of lists of strings is pretty terrible.
The implementation is hugely language dependent so I don't even think pseudo-code would make sense, but I'll try to give the steps for the basic algorithm.
Pop off the first n items of the same type and put them in list.
For each item in list, add that item to schedule set.
Pop off next n items of same type off list.
For each item that starts after the first item ends, put on list. (If none, fail)
Continue until done.
Hardest part is deciding exactly how to construct the lists/recursion so it's most elegant.

Permuting a binary tree without the use of lists

I need to find an algorithm for generating every possible permutation of a binary tree, and need to do so without using lists (this is because the tree itself carries semantics and restraints that cannot be translated into lists). I've found an algorithm that works for trees with the height of three or less, but whenever I get to greater heights, I loose one set of possible permutations per height added.
Each node carries information about its original state, so that one node can determine if all possible permutations have been tried for that node. Also, the node carries information on weather or not it's been 'swapped', i.e. if it has seen all possible permutations of it's subtree. The tree is left-centered, meaning that the right node should always (except in some cases that I don't need to cover for this algorithm) be a leaf node, while the left node is always either a leaf or a branch.
The algorithm I'm using at the moment can be described sort of like this:
if the left child node has been swapped
swap my right node with the left child nodes right node
set the left child node as 'unswapped'
if the current node is back to its original state
swap my right node with the lowest left nodes' right node
swap the lowest left nodes two childnodes
set my left node as 'unswapped'
set my left chilnode to use this as it's original state
set this node as swapped
return null
return this;
else if the left child has not been swapped
if the result of trying to permute left child is null
return the permutation of this node
else
return the permutation of the left child node
if this node has a left node and a right node that are both leaves
swap them
set this node to be 'swapped'
The desired behaviour of the algoritm would be something like this:
branch
/ |
branch 3
/ |
branch 2
/ |
0 1
branch
/ |
branch 3
/ |
branch 2
/ |
1 0 <-- first swap
branch
/ |
branch 3
/ |
branch 1 <-- second swap
/ |
2 0
branch
/ |
branch 3
/ |
branch 1
/ |
0 2 <-- third swap
branch
/ |
branch 3
/ |
branch 0 <-- fourth swap
/ |
1 2
and so on...
The structure is just completely unsuited for permutations, but since you know it's left-centered you might be able to make some assumptions that help you out.
I tried working it in a manner similar to yours, and I always got caught on the fact that you only have a binary piece of information (swapped or not) which isn't sufficient. For four leaves, you have 4! (24) possible combinations, but you only really have three branches (3 bits, 8 possible combinations) to store the swapped state information. You simply don't have a place to store this information.
But maybe you could write a traverser that goes through the tree and uses the number of leaves to determine how many swaps are needed, and then goes through those swaps systematically instead of just leaving it to the tree itself.
Something like
For each permutation
Encode the permutation as a series of swaps from the original
Run these swaps on the original tree
Do whatever processing is needed on the swapped tree
That might not be appropriate for your application, but you haven't given that many details about why you need to do it the way you're doing it. The way you're doing it now simply won't work, since factorial (the number of permutations) grows faster than exponential (the number of "swapped" bits you have). If you had 8 leaves, you would have 7 branches and 8 leaves for a total of 15 bits. There are 40320 permutation of 8 leaves, and only 32768 possible combinations of 15 bits. Mathematically, you simply cannot represent the permutations.
What is wrong with making a list of all items in the tree, use generative means to build all possible orders (see Knuth Vol 4), and then re-map them to the tree structure?

Resources