Sounds a lot like “draw the rest of the damn owl.” [0] :-)
DBMSs and file systems use (extremely sophisticated) tree structures under the hood. Not sure that “linked lists are useful if the linking topology is much more complicated than a simple line” is a ringing endorsement of them.
( Here's an enjoyably lucid explanation on how to draw such an owl: https://www.youtube.com/watch?v=aZjYr87r1b8 )
You actually don't. You can just keep two lists within the same structure, one for occupied nodes and one for free nodes, and just move deletions to the head of the free nodes list.
Example List:
Data: [ a, 0, c, d, e ]
Links: [ 2, -1, 3, 4, -1 ]
Head of occupied nodes: 0
Head of free nodes: 1
Link shape: Occupied Nodes: 0 -> 2 -> 3 -> 4 -> []
Free Nodes: 1 -> []
To Delete C: data[2] = empty // free data
links[2] = 1 // Repoint 2 -> 1
links[0] = 3 // Repoint 0 -> 3
firstFreeNode = 1
This changes the data such: Data: [ a, 0, 0, d, e ]
Links: [ 3, 2, -1, 4, -1 ]
Head of occupied nodes: 0
Head of free nodes: 2
New shape: Occupied Nodes: 0 -> 3 -> 4 -> []
Free Nodes: 2 -> 1 -> []It depends on the use case.
If you're doing a lot of navigating over links before you reach the destination, maybe you want them separate. Could also be the data is large, even maybe stored on disk while the links are in memory, or whatever. There's a thousand different scenarios with different optimal arrangements.
It also allows you to keep multiple different lists to the same data, say sorted by different fields in the struct.
This is a linked list with the same shape as the list above, but with completely different performance characteristics:
With some elaboration (say exchange the arrays for a mmap-call, turn it into a tree instead of a list) and you're basically looking at the guts of a DMBS or a file system.