i would like to ask how is it possible to merge two unsorted lists into one unsorted list in constant time in C since we need a while loop to get all the elements.
ex:
List1: 2 5 1 4 3
List2: 5 9 4 2 5 7 8
List3: elements of the two lists,don't care about order
Don't judge me i'm a beginner.
This depends on the data structure in memory and whether you can modify the existing lists.
If you can modify the existing lists, then you can ask List1 for it's last element (which is O(1) is the list header has a pointer to the end of the list) and then it's simply a matter of List1->last->next = List2->head. Afterwards, iterating over List1 will iterate over all elements.
If you must not change List1, then you have to copy the list; this is tricky to do with O(1) but still possible if you keep all elements in a single memory area (i.e. you don't use pointers to nodes; instead you keep all nodes in an array). In this case, you allocate memory for the nodes of both lists and then you can populate the result with two times memcopy(). Granted, memcopy() isn't really O(1) but with current CPUs (which can copy gigabytes per second), you usually don't notice the difference.
tl;dr: go and read about linked list data structures. All this stuff is perfectly common.
The minimal requirement for a genuinely O(1) append is that you have mutable linked lists with constant-time access to the tail.
So, the simplest possible linked list is:
struct ListNode {
struct ListNode *next;
int data; /* or void*, or whatever */
};
typedef struct ListNode *SinglyLinkedList;
ie, you just hold a pointer to the first element of your list. In this case, getting to the tail is linear time (O(n)), so you can't do what you want. However, if instead we use
struct ListHeadTail {
struct ListNode *head;
struct ListNode *tail;
/* could keep length here as well, if you want it */
};
then inserting to the list is slightly harder, but you can easily do a constant-time append:
struct ListHeadTail append(struct ListHeadTail *first,
struct ListHeadTail *second) {
struct ListHeadTail result;
/* special cases first, where either first or second is empty */
if (first->head == NULL) {
result = *second;
second->head = second->tail = NULL;
} else if (second->head == NULL) {
result = *first;
first->head = first->tail = NULL;
} else {
result.head = first->head;
result.tail = second->tail;
first->tail->next = second->head;
first->head = first->tail = NULL;
second->head = second->tail = NULL;
}
return result;
}
Other common structures are doubly-linked lists - again with a sentinel node rather than a having head->prev == tail->next == NULL.
If what you desire is for something to behave as a concatenated list, you can indeed do so by creating a ConcatenatedList class that is constructed from two existing List classes. If you are working with pure C as opposed to C++, the notion of a class may need to be fudged a bit, but structurally, the idea is the same.
The ConcatenatedList class can have two attributes: a header attribute, which is nothing more than a reference to the first List, and a tail attribute, which is nothing more than a reference to the second List.
You then mimic the methods of the basic List class with ConcatenatedList. To access the kth element of the ConcatenatedList, you try something like this:
if (k < header->size()) {
return header->getElement(k);
}
return tail->getElement(k - header->size());
The rest of the coding should be straight-forward from there.
Using this approach, the concatenation process will be O(1).
It is worth noting that the answer presented by (not) Useless is also valid, but it assumes a LinkedList structure to your lists, whereas this approach does not have that requirement. It is possible however, that after repeated concatenations, this approach will begin to look like a LinkedList implementation in terms of performance.
Related
I am reading about hashing in Robert Sedwick book on Algorithms in C++
We might be using a header node to streamline the code for insertion
into an ordered list, but we might not want to use M header nodes for
individual lists in separate chaining. Indeed, we could even eliminate
the M links to the lists by having the first nodes in the lists
comprise the table
.
class ST
{
struct node
{
Item item;
node* next;
node(Item x, node* t)
{ item = x; next = t; }
};
typedef node *link;
private:
link* heads;
int N, M;
Item searchR(link t, Key v)
{
if (t == 0) return nullItem;
if (t->item.key() == v) return t->item;
return searchR(t->next, v);
}
public:
ST(int maxN)
{
N = 0; M = maxN/5;
heads = new link[M];
for (int i = 0; i < M; i++) heads[i] = 0;
}
Item search(Key v)
{ return searchR(heads[hash(v, M)], v); }
void insert(Item item)
{ int i = hash(item.key(), M);
heads[i] = new node(item, heads[i]); N++; }
};
My two questions on above text what does author mean by
"We could even eliminate the M links to the lists by having the first nodes in the lists comprise the table." How can we modify above code for this?
"we might not want to use M header nodes for individual lists in separate chaining." What does this statement mean.
"We could even eliminate the M links to the lists by having the first nodes in the lists comprise the table."
Consider Node* x[n] vs Node x[n]: the former needs an extra pointer and on-insertion memory allocated for the head Node of every non-empty element, and an extra indirection for every hash table operation, while the latter eliminates the n pointers but requires that any unused elements will be able to be put in some discernable not-in-use state (tracking of which may or may not require extra memory), and if sizeof(Node) size is greater than sizeof(Node*), it may be more wasteful of memory anyway. The difference in memory use can also affect efficiency of cache use: if the table has a high element to buckets ratio then a Node[] gets the Node data into fewer contiguous memory pages, and if you're iterating (in unsorted order) then it's very cache efficient, whereas Node*[] will jump to separate memory allocations that might be all over the place (or on the other hand, might actually be quite close together in some actually useful: e.g. if both access patterns and dynamic memory allocation addresses correlate to chronological time of object creation.
How can we modify above code for this?
First, your existing code has a problem: heads[i] = new node(item, heads[i]); overwrites an entry in the hash table without first checking if it's empty... if there's anything there then you should be adding to the list, not overwriting the array.
The design change discussed needs:
link* heads;
...changed to...
node* head;
You'd initialise it like this:
head = new node[M];
Which needs an extra node constructor (if item has an equivalent default constructor, you can leave out its initialisation below)
node() : item(nullItem), next(nullptr) { }
Then there's some knock on changes to the rest of your code that are easy to work through. Basically, you're getting rid of a layer of pointers.
"we might not want to use M header nodes for individual lists in separate chaining." What does this statement mean.
I didn't write it so can't say authoritatively, but it appears to be saying that when designing the list code, a decision might have been made to have an initial Node even in an empty list, as this simplifies code for several list operations. While the extra data-less Node might seem a reasonable price when contemplating "usual" uses of a list, hash tables are unusual in that you want most of the lists chained of the buckets to have 0 or 1 element, and exponentially fewer should be longer and longer. So, such a list implementation is poorly suited to use in a hash table.
Im trying to use the insertion sort method in order to sort nodes from a LinkedList. I've adjusted the code so many times but I can't quite seem to get it, keep getting different types of results none which are sorted.
Heres the code:
Node* sort_list(Node* head)
{
Node* node_ptr = NULL;
for(Node* i = head->next; i->next != NULL; i = i->next){
if (i->key < head->key) {
node_ptr = i;
head = head->next;
}
}
return node_ptr;
}
This is a homework problem so instead of outright writing a code, I will first point out where you went wrong.
In an insertion sort like algorithm, obviously there needs to be some kind of swapping that needs to be done between elements that are out of place (that is need to be inserted). Hence start with thinking about how you can swap two elements of the array. Pay special attention to the cases when one is head or one is tail.
Your implemented code doesn't have any trace of pointer swaps so this is where you are wrong.
Next you must think about the cases when we need to sort. In this case, it is rather simple. If the current element and the next are in sorted order (assuming ascending order, current < next). Then nothing needs to be done but simply make the next one the current.
Then you can obviously infer that violation of this case is when you need to swap the elements. After the swap (with proper attention to where the pointers were and will be after sorting), repeat the process till you hit the null wall.
P.S : This is a possible duplicate of another SO question.
So i have a singly linked list. New items are added to the front of the chain, so if you added 8,4,10 the list would be 10,4,8. Anyway, now I am trying to sort the list after the insertion is completed except I just cannot figure out how to then loop through these numbers and re-arrange them in ascending order. I'll probably take a break here and come back in a bit, hopefully that will help me figure this out.
*this is a project for school, so suggesting I use some other container is not helpful in my case except being informative as I cannot change what I am working with.
Layout of the list
struct Node
{
int Item; // User data item
Node * Succ; // Link to the node's successor
};
unsigned Num //number of items in the list
Node * Head //pointer to the first node
My insertion function looks like this
Node * newOne;
newOne = new (nothrow) Node;
newOne->Item = X;
newOne->Succ = NULL;
if(Head == NULL)
{
Head = newOne;
}
else
{
newOne->Succ = Head;
Head = newOne;
}
Num++;
This can be done two ways, i am not going to post the code, but hopefully this will help you.
1. Arrange in ascending order during insertion
This involves adding the elements always in ascending order. For example if the link list is
| 1 |
and you add 5 the new link list is
|1|--->|5|
and if you add 3 next it should be
|1| ---> |3| ----> |5|
This involves comparison of the new element till you find the correct position.
2. Wait till all the elements are inserted, arrange in ascending order.
This would involve using a sorting algorithm like merge sort, insertion sort, bubble sort etc on the elements of linklist.
After the sorting of the numbers is done, rewrite the linklist in the correct order.
Example:
if link list contains
3 2 5 1 6
After sorting through an algorithm
1 2 3 5 6 (stored in an array)
Now loop through the link list and replace the elements in correct order.
Be careful if the nodes contain other elements, those other elements would need to be replaced too.
Note: This requires extra memory, the other way would to swap the nodes which would take longer time. Unless the link-list contains a large number of nodes(thus making memory important) or has other elements, using array would be simpler.
Sorting a singly linked list is nasty to code (unless you like linear sorts such as bubble sort or insertion sort). It's much simpler to copy the contents into a vector, sort the vector, and then rebuild the list.
I am reading about list traversals in Algorithms book by RobertSedwick. Function definitions are shown below. It is mentioned that it is possible to have traverse and remove functions can have iterative counter parts, but traverseR cannot have. My question why traverseR cannot have iterative counter part? Is it that if recursive call is not end of function i.e., like in traverse then we cannot have iterative, Is my understanding right?
Thanks for your time and help.
void traverse(link h, void visit(link))
{
if (h == 0) return;
visit(h);
traverse(h->next, visit);
}
void traverseR(link h, void visit(link))
{
if (h == 0) return;
traverseR(h->next, visit);
visit(h);
}
void remove(link& x, Item v)
{
while (x != 0 && x->item == v)
{ link t = x; x = x->next; delete t; }
if (x != 0) remove(x->next, v);
}
traverseR uses the call stack to store pointers to all the nodes of the list, so that they can be accessed in reverse order as the call stack unwinds.
In order to do this without a call stack (i.e. non-recursively), you'll need some other stack-like data structure to store these pointers in.
The other functions simply work on the current node and move on, with no need to store anything for use after the recursive function call returns. This means that the tail recursion can be replaced with a loop (either by modifying the code or, depending on the compiler, letting it determine that that's possible and make the transformation itself).
Assuming that the list is single-linked, it is not possible to visit it iteratively in the backward order because there's no pointer from a node to a previous node.
What the recursive implementation of traverseR essentially does is that it implicitly reverses the list and visits it in the forward order.
You could write and iterative version of traverseR using a stack: in a loop iterate from one node to another, pushing the nodes on the stack. When you get to the end of the list then, in another loop, pop and visit the nodes you visited.
But his is basically what the recursive version does.
It is possible to traverse a singly linked list in reverse order with only O(1) extra space -- i.e., without a stack of previously visited nodes. It is, however, a little tricky, and not at all thread safe.
The trick to this is to traverse the list from beginning to end, reversing it in place as you do so, then traverse it back to the beginning, reversing it again on the way back through.
Since it is a linked list, reversing it in place is fairly straightforward: as you get to a node, save the current value of its next pointer, and overwrite that with the address of the previous node in the list (see the code for more detail):
void traverseR(node *list, void (*visit)(node *)) {
node *prev = nullptr;
node *curr = list;
node *next;
if (!curr)
return;
// Traverse forwards, reversing list in-place as we go.
do {
next = curr->next;
curr->next = prev;
prev = curr;
curr = next;
} while (curr->next);
// fix up so we have a fully reversed list
curr->next = prev;
prev = nullptr;
// Traverse the reversed list, visiting each node and reversing again
do {
visit(curr);
next = curr->next;
curr->next = prev;
prev = curr;
curr = next;
} while (curr->next);
}
Like almost anything dealing with linked lists, I feel obliged to add that (at least IMO) they should almost always be treated as a purely intellectual exercise. Using them in real code is usually a net loss. You typically end up with code that's slow, fragile, and hard to understand, as well as typically wasting quite a bit of memory (unless the data you store in each node is pretty big, the pointer can often use as much space as the data itself).
My question why traverseR cannot have iterative counter part? Is it that if recursive call is not end of function i.e., like in traverse then we cannot have iterative, Is my understanding right?
Correct. The functions traverse and remove end with a call to themselves. They are tail recursive functions. The call in traverseR to itself is not at the end of the function; traverseR is not tail recursive.
Recursion in general has an expense of creating and later destroying stack frames. This expense can be completely avoided with tail recursive functions by changing the recursion into iteration. Most compilers recognize tail recursive functions and convert the recursion to iteration.
It is possible to write an iterative version of traverseR depending on what you mean by iterative. If you are limited so a single traversal through the list, it is not possible. But if you can sacrifice a lot processing time it can be done. It does use less memory in the classic speed vs. memory trade-off.
void traverseRI(link h, void visit(link))
{
if (h == 0) return;
link last = 0;
while (last != h)
{
link test = h;
while (test->next != last)
{
test = test->next;
}
visit(test);
last = test;
}
}
int LinkedList::DoStuff()
{
Node *Current = next_;
while ( Current != NULL )
{
Current = Current->next_;
length_++;
}
// At the last iteration we have reached the end/tail/last node
return length_;
}
there are no more nodes beyond the last. How can i traverse to the tail-end to the front-head?
Unless your linked list is a doubly-linked one, this is difficult to do. Recursion is one way, assuming you don't have lists so big that you'll run out of stack space, something like this (pseudo-code):
DoStuffBackwards (currNode) {
if (currNode != NULL) {
DoStuffBackwards (currNode->next);
// Process currNode here.
}
}
DoStuffBackwards (firstNode);
This works because you keep calling DoStuffBackwards() for the next node until you exhaust the list then, as you roll back up the recursion stack, you process each node.
If you just want to go backwards from last node to current node, than Pax's answer (using recursion) is your best bet, also see my version below. If your current node is not the head of your non-circular-singly-linked-list, and you want to go from current node to head node, it is impossible.
int LinkedList::DoStuff()
{
return DoStuffBackward(next_, 0);
}
int LinkedList::DoStuffBackward(Node* node, int n)
{
if (!node)
{
return n;
}
int len = DoStuffBackward(node->next_, n + 1);
std::cout << "doing stuff for node " << n << std::endl;
return len;
}
This has the smell of homework, so no code, but here's an overview of a solution that doesn't require recursion:
If you want to run through the list backward one option to relink the list to point backwards as you're traversing it to find the end. Then as you re-traverse the list (which visits the nodes in the reverse order from the original list) you repeat the relinking same as before and the list ends up in its original order.
This is simple in concept, but handling the pointers and links correctly (especially at the start and end of the list) can be a bit tricky.
Recursion can work, as can building an auxiliary data structure, such as an array with one entry for each element of the original list. If you want a solution for a single-threaded list without requiring O(n) extra storage, the best bet is to reverse the list in place as Michael suggests. I wrote an example for this, [but I'll leave it out given the concern about homework]. One caution about reversing the list: if there are any other data structures that hold pointers to the original list, and you might be accessing them during your traversal, they won't work if they need to access the list while it's reversed, and this might lead to data corruption if they try to modify the list.
Update: Ok, here's the (C++) routine to reverse a list in place. It hasn't been tested, though. And I'll note that if this is homework, the poster still needs to figure out how to use this routine correctly to get a complete answer.
Node *ReverseList(Node *head) {
// Reverse a single-threaded list in place, return new head
Node *prev=NULL;
Node *cur=head;
while (Node *next=cur->next_) {
cur->next_ = prev;
prev = cur;
cur = next;
}
cur->next_ = prev;
return cur;
}
push the list on a stack and then pop them off.
Is your linked list class doubly-linked or singly-linked? If there is no previous pointer inside each node, you can't traverse backwards.
I also suggest you post more code and take the time to make your question readable.