Thank you all for your answers! I really understand that this question was asked almost two years ago, and the answer has long been accepted, but I was a little confused by the answers. Thus, although the questionnaire probably does not care about the new answers, I would like to add my version if other readers are confused and document my own approach. Some of them may have been more suitable as comments, but I do not yet have a reputation for comment.
Firstly, no matter how often I look at it - on the board or in the debugger - I cannot avoid ending the loop in the first node, that is, pointing to myself, if I do not use to conditionally distinguish between cases of mixing nodes, and not even with abstract steps or specific code from Smashery's currently accepted answer. I searched the Internet a bit to find the code in the style of the currently accepted answer in order to avoid such a conditional, but for me it is surprisingly difficult to avoid, and my searches did not find such a solution (if I am not mistaken about the proposed, possibly incorrect). If there was some kind of clever expression that would give the first node address when they are adjacent, and the first node prefix when they are not, this condition is not needed, because the second new node successor is found out which (apparently) requires conditional.
Secondly, I have the same question about successive assignments to the same variables in the accepted answer as other commentators. I hope I'm not very dense here, but assigning different values to the same variable in the sequence, unless, perhaps, in case of side effects, it just never seems to me that she left the variable with any other value than the last assignment, regardless on what configuration of nodes I am considering, and thus makes previous assignments clearly redundant. If I am wrong about this, and this approach will actually solve this problem, then I could eliminate the last condition in the code below, which I tried to get rid of when I first saw the solution on the Internet for replacing nodes without adjacent nodes of a special casing string. I'm not quite sure, but it sounded like Smashery deliberately left these repetitive tasks out of logical rigor and better illustrated the procedure — I might have misunderstood.
Thirdly, on this and other sites I often saw that the assertion from other answers repeated that it is better to exchange the contents of nodes, rather than pointers. Of course, in the case of prime integers, as in the examples above, this apparently gives a shorter, simpler code. However, when we discuss linked lists with nodes containing integers, it is usually a backup for the backup data structure of a more complex and universal container. Thus, I do not think that replacing the contents of nodes is actually so simple, at least if the implementation of the data structure cannot make assumptions about the semantics of the instance of the container elements. In addition, the ability to exchange the contents of nodes like me implies that the linked list has the right to own the contents of these nodes, because otherwise the code outside the linked list method may contain links to objects in these nodes whose values suddenly change under them.
I acknowledge, however, that this may depend on the semantics of the container. For an array, you can expect the swap method to change the value under links to a specific index of that array. This means that the links are not intended to indicate a specific object, but rather for a position in a container that can be indexed. If we consider the linked list as a means only to order a set of objects that use them outside of the linked list, the user probably expects the swap operation to replace only the position, not the content.
Imagine, for example, that a linked list represents objects of the type “car”. Each car has an owner, and this owner refers to his car through a pointer to it. Now suppose that the linked list is an order for a set of cars that must be serviced for inspection at a car dealership. If we change the contents of two nodes to exchange slots for two cars and do this by exchanging their contents, then the service will really happen in the new correct order - but people will also end up with different cars! (I would not want to swap places with Tesla, because I just drove Corolla.)
If the linked list was, as in the array example, based on indexing semantics, then the position in the node can simply represent the order in which the cars are loaded onto the ship for transportation. At the moment, the cars have no owners, and we really only care about which slot they are in. Then, I believe, it really doesn’t hurt to change cars, that is, to the contents of the objects referenced by the nodes.
Finally, to the code. As I said above, I could not avoid a special shell for neighboring nodes.
First, the definition of a helper method:
int find_node(int v, node* root, node** n, node** pn) { int i = 0; for (*n = root; *n != NULL && (*n)->v != v; ++i) { *pn = *n; *n = (*n)->next; } return i; }
This method finds node by its value. The returned integer is a position based on a zero value (call it the index, if you want) node in the linked list. I found that position adjacency detection, not pointer comparison, is more readable. The beginning of the list is root. The method sets n to point to the node containing the passed value. In pn, the method stores the predecessor of n.
The following is the actual swap:
void swap_nodes(node **root, int v1, int v2) { if (v1 == v2) return; node *n1, *n2, *pn1, *pn2; int p1 = find_node(v1, *root, &n1, &pn1); int p2 = find_node(v2, *root, &n2, &pn2); if (p1 > p2) { std::swap(n1, n2); std::swap(pn1, pn2); std::swap(p1, p2); } if (p1 == 0) *root = n2; else pn1->next = n2; node* nn1 = n1->next; n1->next = n2->next; if (p2 - p1 > 1) { n2->next = nn1; pn2->next = n1; } else { n2->next = n1; } }
Sorry, I changed the OP method signatures a bit. I found it more convenient to pass the values of nodes for exchange, as opposed to node pointers. If you pass the node pointers only to the corresponding nodes, you will have to do another workaround to find the predecessors with this solution, which was a bit inconvenient for me. If we cannot distinguish nodes by these values, for example. values are not unique, we need pointers to nodes.
As with the description of find_node above, we first find the positions, nodes, and predecessors for the node values passed to swap_nodes via v1 and v2. The values for the first and second nodes are replaced if the second node replacement appears before the first. This is not much code for this, it reduces the special case and makes it a little easier to visualize.
Now we have only two conditional expressions left, none of which seemed trivial. If the first node is at the head of the linked list, that is, at the zero position, the root node must point to the second node. Otherwise, the first predecessor node will point to the second node.
The previous value of the first successor node must be remembered if the nodes are not adjacent. Then the first successor node is installed on the current successor of the second node. This is the only change that applies to all cases: the new successor of the first node, which is the old successor of the second node, is the only certainty and useful to start with, to remember the pointer operations and their sequence when implementing this swap.
Finally, if the positions of the nodes differ by more than one, they are not adjacent. Then the second node successor becomes the first old node successor - saved above - and the second node predecessor now points to the first node. If they are adjacent, there are no nodes between nodes that require updating, so just binding the second node to the first is all that remains to be done.