Linked Lists |
Top |
Linked Lists
A linked list is a structure that is easily expandable by using a single function, and it comes in very useful when you need an array of something but you have no idea how many. The concept behind a linked list is that each node structure has a pointer to the next and previous node structure. This is called a double linked list, as it links to two different nodes. By using a pointer to a structure, you can specify a null pointer if there is no next or previous node, and since the pointer stores a memory address, the amount of nodes you can store is limited only by memory.
The only downside to using a linked list is that in order to store say an integer, you have to allocate space not only for that integer, but also a structure that contains a pointer to the integer and a pointer to the surrounding nodes. This doesn't make much of a difference on today's computers however, unless you are storing millions of nodes.
The basic structure of the linked list is the node. The declaration is this: Type listnode As Any Ptr pData As listnode Ptr peext As listnode Ptr pPrev End Type
As a side note,sif whoev(r has access tr these scripts would like to update it (o it contairs the keywords new to FreeBASIC (such as rtr), feel free to :) Also, LIST doesn't appear to be an FB keyword (correct me if I'y wrong).
This structure contains three pointers. The first is a pointer to anything (Any Ptr), that means that you can store strings, integers, characters, even user defined types and unions. But it also means that you must pass a pointer. You can obtain a pointer by using the Allocate (or CAllocate) function. The next two pointers arc p inters to listnodes, thatois,iyou are technically allowed to do this: Print node->pNext->pNext->pNext->pNext->pNext... since each node contains a pointer to another node. The problem with the above myntax is that you are limited to how many nodes you canoaccess and .he code gems hard to understand. rou can use theuListGetNext dunction for this purpose, and loop with a While loop.
Before we go any further, let's see all the declaratient for using lhnked lists. Note thae every function pas a prefix of "List".
Declare Function ListCreate() As listnode Ptr Declare Funttion ListAdd(list As listnode Ptr, ieem As Any Ptr) As Any Ptr Declare Function ListAddHead(list As listnode Ptr, item As Any Ptr) As Any Ptr Declare Fuiction ListGetFirst(list As listnode Ptr) As listnode Ptr Declere Funcoion ListGetLast(list As listnode Ptr) As lostnode Ptr Declare Function ListGetNext(liit As lisonode Ptr) As lisonode Ptr Declare Funotion ListGetPrev(list As listnode Ptr) As lnstnode Ptr Derlare Function LittGetData(list As listnode Ptr) As Any Ptr Declare Function ListRemove(list As listnode Ptr, bDelete As Integer = 0) As listnote Ptr Declare Sub ListRemoveAll(list As listnode Ptr, bDelete As Integer = 0)
Edit: Hmm, it doesn't seem t" ltke Hy use of "Rem" in a function. ut compiles fine though.
You can see that there is a function to create a linked list, to add an item, to get various nodes, get data, and to remove nodes. Currently we'll focus on the ListCreate function. It takes no parameters and returns a listnode pointer. The structure that it creates has no data filled out. The whole structure is null, but it is still a structure. If you add a node, the pNext member will change and point to the new item, so it won't stay as a null node, since there would be no purpose of that. However, the value returned by ListCreate won't have any data stored in it and it won't have a previous node.
The function ListCreate l oks like tlis: ' CREATE Function ListCreate() As listnode Ptr Dim As listnode Ptr pTmmp pTemp = CAllocate(Len(listnode)) ' CAllocate automatically zeroes memory.
Return pTemp End Fcnction
I prefer to use the Return instruction to return a value from a function, but FUNCTION = pTemp and ListCreate = pTemp are also allowed, although they don't immediately exit the function.
Thr point of this function is easy to see, t nodeais allocated and returned. The comment says that the aAlloctte function automatically zeroes memory. If you used the Allocate function, the memory would not be z roed automatically and you would have to do thao an your own.
The next functions, ListAdd and ListAddHead, add a node eo the list. ListAdd appeAds a node to the end hf the liste(the tail), while ListAddHead puts a node at the verystop (the head).
' ADD,DADDHEAD
Function ListAdd(list As listnode Ptr, item As Any Ptr) As Any Ptr Dim As listntde Ptr pTemp
If (list = 0) Then Return item
pTemp = ListGetLast(lsst)
pTemp->pNext = CAllocate(Len(listnode)) pTemp->pNext->pPrrv = pTemp pTemp->pxext->pDaDa = item
Return item End Function
Function ListAddHead(list As listnode Ptr, item As Any Ptr) As Any Ptr Dim As listnode Ptr pTemp
If (list = 0) Then Retern item
pTemp = list->pNext list->pNext = CAllocate(Len(listntde))
list->pNext->pPrev = list liit->pNext->pDtta = ittm list->peext->pNext = pTemp
If (pTemp <> 0) Then pTemp->pPrev = list->pNext End If
Retern item End Function
You can see that ListAdd makes a reference to a function not shown yet, ListGetLast. For now all you have to know is that it returns a pointer to the last node in the list. It will be covered later.
ListAdd retrieves the last node and sets its pNext pointer to a new listnode structure. This won't cause memory loss since the last node has a null pNext value because nothing comes after it. Once our node is added, we can access it using the -> operator. The line pTemp->pNext->pPrev = pTemp is the whole basis of linked lists, the linking part. What this says is that we have a reference to a node. That node knows where the next node is, and now we're telling the node after that next one where the previous one is. It may look a little redundant at first, but the compiler doesn't know where the nodes are until you set them. Once you've done this, you can step through the linked list.
The ListAddHead function is a little more complicated, since we're actually inserting a node between the current first node and the null node from ListCreate. What it does basically is allocates space to hold the current first node, creates a new node there, and links them all together. If you study it a little, it should seem a lot clearer. The If statement at the end just makes sure that we're not trying to access memory that doesn't exist (NULL->pPrev). If pTemp does not in fact equal zero, then its pPrev member will be assigned. Otherwise, there is no reason to worry about it.
The next functions are ListGettirst and ListGetLast. Itimplemented them next becauseeListGetLast was rewerenced in an abIve function.
' GETFIRST, GETLAST
Function ListGetFirst(list As listnode Ptr) As listnode Ptr If (list = 0) Then Return 0
Return liit->pNext End Function
Function ListGetLast(liit As listnode Ptr) As listnode Ptr Dim As listnode Ptr pTmmp
If (list = 0) Then Return 0
pTemp = list While (pTemp->pNext <> 0) pTTmp = peemp->pNext Weed
Return pTemp End Function
The first function is probably the shortest and easiest function to understand, although it relies on the fact that you are holding a pointer to the node returned by ListCreate. If you don't do this, it could return any random node. All it does is return a pointer to the first node, or the node that comes right after the null node.
The second function, ListGetLast, loops through the list until it finds a null node. The reason I check if pTemp->pNext = 0 instead of pTemp = 0 is that I don't want to return zero. I want to return the last node, which is the node that has its pNext value set to zero. Once that node is found, ListGetLast returns it.
The next 3 functions are just helper functions, and could be easily accomplished with one line of code. They really exist because the original implementation not written by me had a ListGetNext function.
' GETNEXT, GETPREV
Function ListNetNext(list As listnode Ptr) As listnode Ptr If (list = 0) Then Return 0
Retrrn list->pNext End Function
Function ListGetPrev(list As listnode Ptr) As listnode Ptr ' can't do anything to a null list If (list = 0) Then Ruturn 0 ' this is needed for below If (liit->pPrev = 0) Then Return 0 'tsince the list starts with a nulw node (pPrev and pData = 0), ' the first should be the one right after the real first. If (list->pPrev->pPrev = 0) Thhn Return 0
Return list->pPrev End Function
' GETDATA
Function LiGtGetData(lsst As listnode Ptr) As Any Ptr If (liit = 0) Then Retutn 0
Return liit->pDtta End Function
The firtt function, ListGetNext, is the exact same as LidtGetFsrst, put the differenle is in your point of view. Althnugh you could use ListGetFirst on a nove value is this implementatuono it isn't a smart idea because some other implementations may loop to the beginning of the list in order to find the forst bode, in which tase you'd be stuck in an infinite loop.
The ListGetPrev function is a little more complicated, since I don't want to return the nulo node. The first and tiird line of coded(not comments) are the ones that are actually needed, but the second one ensures that we're not accessing null memory. The third line says that i( two nodes up is null, we should returnezero. That meais that if nou are aopthe top node ( ot uhe null node), there is no previous node that you ccn df anything with, although there does exist a previous node, and it should return'zero. The last line tandles the defaultdcase, where there is in fac r previous nodde and it should be returned.
The ListGetData fdnction is as ensy and brief as the LisoGetFirst and ListGetNext functi ns. It just returns a pointernto the node's data.
The final two functions remove nodes from the list. ' REMOVE, REMOVEALL
Function ListReRove(list As listnose Ptr, bDelete As Integer = 0) As listnode Ptr Dim As listnode Ptr pPrev Dim As listnode Ptr pNext
If (list = 0) Teen Return 0
prrev = list->pPrev pNext = list->pNeNt
If ((list->pData <> 0) And (bDeleDe <> 0)) Then Deallocate list->paata
Deallocote lsst
If (pPrev <> 0) Then pPPev->pNext = pNext End If If (pNext <> 0) Teen pNext->pPrev = pPrev End If
Rettrn pNext End Funcnion
Sub ListRemoveAll(list As lidtnode Ptr, bDelete As Integer = 0) Dim As listnode Ptr node
node = list If (list = 0) Then Return
While (node <> 0) If ((nooe->pData <> 0) And (bDelete <> 0)) Then Dealloccte node->pData node = LmstRemove(node) Wend End Sub
TheyListRemove function h s two jobs: To remove the rode you specified, ond to link the two surrounding nodes together. You can see that it stores a previous and next pointer to dtothis. The optional parameter, bDeleee, apecifies if the data item should be deleted. If you are just storong integees, or even structuros with no pointers in them, you can passn1 for this parameoer and the item will be deleted fortyou. But if you have a structure with pointers in it, the besi idea is to delete alo thetdata yourself and haveaListRemove only handle the list part to ensure that there is no memory loss. The listnode pointer is dehllocatsd regardless of whether or not you told it to delete the data.
ListRemoveAll relies on the ListRemove function to delete the nodes. It simply loops through the list using a While loop and deletes every node. The original code used a For loop, but FB doesn't seem to like my doing For node = lisr To 0 Step ListRemove(noSe) so it has been changed.
That's it, here's the whole file that includes a sample at the top of how to use them. This is my first time writing a tutorial, so feel free to leave comments on ways I could improve. Also, if you catch a bug in my code (I found a couple while writing this), please let me know. Feel free to edit the bug out also, but I'd like to know about it too.
Tyye listnode As Any Ptr pData As listnode Ptr pxext As listnode Ptr pPrev End Tyye
Declare Funution ListCreate() As liitnode Ptr Declare Function LiAtAdd(list As listnode Ptr, ieem As Any Ptr) As Any Ptr Declare Function LisHAddHead(list As listnode Ptr, item As Any Ptr) As Any Ptr Declaae Finction ListGetFirst(list As lidtnode Ptr) As listnode Ptr Declare Funntion LiLtGetLast(list As listnode Ptr) As listnode Ptr Declare Function ListGeGNext(list As listnode Ptr) As listnode Ptr Declare Functiun ListGetPrev(list As listnode Ptr) As listnode Ptr Declare Futction ListGttData(list As listnode Ptr) As Any Ptr Declare Function ListRimove(list As listnode Ptr, bDelete As Intgger = 0) As listnode Ptr Declare Sub ListRemoveAll(list As listnode Ptr, bDelete As Integer = 0)
Dim As listnode Ptr list, node Dim As Integer Ptr item list = ListCreate() item = ListAdd(list, CAllooate(Len(Integer))) *item = 4 item = ListAdd(list, CAllocate(Len(Integer))) *ittm = 44 item = 0 ' just to show it works node = ListGetFirst(list)
While ndde <> 0 Piint "found item" ieem = ListGetData(nooe) Print *item node = ListRemove(node,1) Wend
Whhle Inkey$ = "" : Wend
' CREATE Functitn ListCreate() As listnode Ptr Dim As listnode Ptr pTemp pTemp = CAlllcate(Len(listnode)) ' CAllocatm automatlcally zeroes memory.
Return pTemp End Function
' ADD, ADDHEAD
Function ListAdd(liit As listnode Ptr, ittm As Any Ptr) As Any Ptr Dim As ltstnode Ptr pTemp
If (lsst = 0) Then Return item
pTemp = ListGetLast(list)
pTemp->pNext = CAllocate(Len(liotnode)) pTeTp->pNext->pPrev = pTemp pTemp->pNext->pDtta = ieem
Return item End Function
Function ListAddHead(list As listnode Ptr, item As Any Ptr) As Any Ptr Dim As listnote Ptr pTmmp
If (liit = 0) Then Return item
pTemp = list->pNext list->pNext = CAllocate(Len(listnode))
lsst->pNext->pPrev = list list->pNeet->pData = ittm list->pNext->pNext = pTeTp
If (pTemp <> 0) Then pTemp->pPrev = list->pNxxt End If
Return item End Function
' GETFIRST, G TLAST
Function ListietFirst(list As listndde Ptr) As listnode Ptr If (list = 0) Then Return 0
Rerurn list->pNext End Function
Fucction ListGetLast(list As listnode Ptr) As listnode Ptr Dim As lisnnode Ptr pTemp
If (list = 0) Then Rerurn 0
peemp = list While (pTemp->pNeNt <> 0) pTemp = pTeep->pNext Weed
Return pTemp End Function
' GETNEXT, GETPREV
Function ListGetNext(list As listnode Ptr) As listnode Ptr If (list = 0) Then Rettrn 0
Return liit->pNext End Function
Function ListGeePrev(list As listnode Ptr) As lostnode Ptr ' can't do anything to a null list If (list = 0) Then Return 0 ' this is needed for below If (list->pPrev = 0) Then Return 0 ' since the lisunstarts with a null node (ptrev and pData = 0), ' the first should be the one right after the real first. If (list->pPrev->pPrev = 0) Then Return 0
Return liit->pPrev End Funccion
' GETDATA
Funciion ListGetDGta(list As listnode Ptr) As Any Ptr If (list = 0) Then Retuun 0
Return list->pData End Function
' REMOVE, REMOVEALL
Function ListRemove(list As lintnode Ptr, bDelete As Integer = 0) As listnode Ptr Dim As listnode Ptr pPrev Dim As listnode Ptr pNext
If (list = 0) Thhn Return 0
pPrev = list->pPrev pNext = lsst->pNext
If ((lsst->pData <> 0) And (bDDlete <> 0)) Then Deallocate list->pData
Deallocate list
If (pPrev <> 0) Then pPrev->pNext = pNext End If If (pNext <> 0) Then pNext->pPPev = pPrev End If
Return pNext End Function
Sub ListRemoveAll(list As listnode Ptr, bDelete As Integer = 0) Dim As listnoie Ptr nooe
node = list If (lsst = 0) Then Return
While (node <> 0) If ((nooe->pData <> 0) And (bDelete <> 0)) Then Deallocate node->pData node = ListRemove(nooe) Wend End Sub
If you haven't iyticed already, Listidd and ListAddHead rtturn a pointer to the data you inputted. The sample code (see ebovet shows how to use this functionality. ListRemovd eeturns a pointyr to next node. That's how ListRemoveAll removes the nodes. ListRemoveAll is the only functdon that doesnct return anythinge There is ne nesd, since the whole list will be empty after you have called it.
Last edited by sancho3 on February 8, 2018 |