For example, we have series 1, 2, 3, 4, 5. We take every 3 element => 3, 1, 5, 2, 4 (chosen element shouldn't remain, we can take while series is not empty). Naive implementation by circle doubly linked list is not good idea cause of performance. Can you give me an advice which data structures and algorithms are more applicable?
相关问题
- What is the best way to do a search in a large fil
- Finding k smallest elements in a min heap - worst-
- binary search tree path list
- High cost encryption but less cost decryption
- C++ a class with an array of structs, without know
相关文章
- What is the complexity of bisect algorithm?
- What are the problems associated to Best First Sea
- Coin change DP solution to keep track of coins
- Algorithm for partially filling a polygonal mesh
- Robust polygon normal calculation
- Algorithm for maximizing coverage of rectangular a
- Visual Studio: Is there an incremental search for
- Is there an existing solution for these particular
Here is an implementation with an array representation of the binary tree, only storing the size of the left sub-tree as node value. The input array is not actually stored, but silently assumed to be the leaves at the bottom level, below the binary tree:
Below is an implementation of Lei Wang and Xiaodong Wang's (2013) 1
O(n log k)
algorithm (very similar to, if not based on, the algorithm by Errol Lloyd, published in 1983). The idea is to divide the original sequence inton/m
binary trees of heightlog k
. The algorithm is actually designed for the "feline" Josephus problem, where the participants can have more than one life (listed in the array variable below,global.l
).I also like the
O(1)
space algorithms by Knuth, Ahrens, and Kaplansky, (outlined in a master's thesis by Gregory Wilson, California State University, Hayward, 19792), which take a longer time to process, although can be quite fast depending on the parameters.Knuth’s algorithm for
J(n,d,t)
(t
is theith
hit), a descending sequence:Ahrens’ algorithm for
J(n,d,t)
, an ascending sequence:Kaplansky’s algorithm for
J(n,d,t)
:JavaScript code:
(1) L. Wang and X. Wang. A Comparative Study on the Algorithms for a Generalized Josephus Problem. Applied Mathematics & Information Sciences, 7, No. 4, 1451-1457 (2013).
(2) References from Wilson (1979):
Knuth, D. E., The Art of Computer Programming, Addison-Wesley, Reading Mass., Vol I Fundamental Algorithms, 1968, Ex. 22, p158; Vol. III, Sorting and Searching, Ex. 2, pp. 18-19; Vol. I, 2-nd ed., p.181.
Ahrens, W., Mathematische Unterhaltungen und Spiele, Teubner: Leipzig, 1901, Chapter 15, 286-301.
Kaplansky, I. and Herstein I.N., Matters Mathematical, Chelsea, New York, 1978, pp. 121-128.
If you just need the last number, it's known as Josephus problem and there're well-known formulas for computing the answer in
O(N)
time.I don't know if one can adapt it to run a full simulation, so I'll describe a straightforward
O(N log N)
solution here:Let's keep all numbers in a treap with implicit keys. We need to find the
k
-th element and delete it at each step (in fact, there can be a shift, so it's more like(cur_shift + k) % cur_size
, but it doesn't really matter). A treap can do it. We just need to split it into 3 parts[0, k - 1]
,[k, k]
and[k + 1, cur_size - 1]
, print the number in the node that corresponds to the second part and merge the first and last part back together. It requiresO(log N)
time per step, so it should be good enough for the given constraints.Build a complete binary tree containing the numbers 1 to n, e.g. for n=15 that would be:
In each branch, store the number of nodes to the left of it; this will allow us to quickly find the i-th node. (You'll see that this tree has a very predictable structure and values, and generating it is much more efficient than building a same-sized binary tree with randomly-ordered values. It's also an ideal candidate for a tree-in-an-array.)
Then, to find the i-th number, start at the root node, and at every node, if i is one greater than the number of nodes to the left, you've found the i-th number, else go left (if i is not greater than the number of nodes to the left) or right (if i is more than 1 greater than the number of nodes to the left).
Whenever you go left, decrement the count of nodes to the left of this node (because we'll be removing one).
Whenever you go right, decrease the number you're looking for by the number of nodes to the left of the node, plus 1 (or plus 0 if the value in the node has been erased).
When you've found the i-th node, read its value (to add to the removal order list) and then set its value to 0. Thereafter, if the i-th node we're looking for has had its value erased, we'll go right and then take the leftmost node.
We start with a value i = k, and then every time we've erased the number in the i-th node, we'll decrement the total number of nodes and set
i = (i + k - 1) % total
(or if that is zero:i = total
).This gives a log2N lookup time and a total complexity of N×LogN.
Example walk-through: with n=15 (as in the image above) and k=6, the first steps are 6, 12, 3, 10, 2. At that point the situation is:
We've just removed the second number, and now
i = 2 + 6 - 1 = 7
. We start at the root node, which has 4 nodes to the left of it and still has its value, so we go right and subtract 5 from the 7 we're looking for and get 2. We arrive at node 12 (which has been erased) and find there are 2 nodes to the left of it, so we decrement the number of nodes to the left of it and then go left. We come to node 10 (which has been erased) and find that it has 1 node to the left of it, and 1 = 2 - 1 so this is the node we're looking for; however, since its value has been erased, we go right and subtract 1 from the 2 we're looking for and get 1. We arrive at node 11, which has 0 nodes to the left of it (because it's a leaf), and 0 = 1 - 1, so this is the node we're looking for.We then decrement the total number of nodes from 10 to 9, and update i from 7 to
(7 + 6 - 1) % 9 = 3
and go on to find the third node (which is now the one with value 5).Here's a simple implementation in JavaScript. It solves series of 100,000 numbers in less than a second, and it could probably be made faster and more space-efficient by using a tree-in-an-array structure.
(Unlike in the explanation above, the indexes of the numbers are zero-based, to simplify the code; so index 0 is the first number in the tree, and we look for the node with a number of left-connected children that equals the target index.)
NOTE:
If you look at the tree for n=10, you'll see that the node to the right of the root has an incomplete tree with 2 nodes to its left, but the algorithm as implemented in the code example above gives it an incorrect left-node count of 3 instead of 2:
However, nodes with an incomplete tree to their left never hold a value themselves, and never have nodes to their right. So you always go left there anyway, and the fact that their left-node count is too high is of no consequence.