contributed by < YangYeh-PD
>
Consider the structure node definition,
we can construct a linked list by the following APIs.
list_add()
The first parameter is the indirect pointer list
of the head of the linked list, and the second parameter is the pointer of the node we want to add. Since we directly change the next
of the node points to *list
(the head of the list), then update the *list
points to the new node, the function adds the node onto the front of the linked list.
list_tail()
Again, we pass the indirect pointer left
of the linked list as a parameter into the function.
null
), the while
condition would be false
and breaks the loop, then return the pointer (*left
) directly.next
of the node where *left
points to is empty (or null
), then return the pointer *left
.list_length()
The function again pass the indirect pointer left
as a parameter. We ++n
and reassign left
to the pointer of the next node in each literation, then return n
when the loop breaks.
list_construct()
First of all, we allocate a memory with size of node_t
saved by the pointer node
. Then we modify the next
points to the list
and assign the value n
, and return the pointer node
.
list_free()
We pass the indirect pointer list
as a parameter.
First, we declare the pointer node
and initialize with the next
which points to the next node of the head of the linked list. (Since we cannot access the memories anymore once we free them). Then we free the memory of each nodes until the last one.
1
: Quicksort (Non-recursive)We can make a non-recursive quicksort algorithm by the following functions.
Before making a list of code snip, describe the concepts and considerations!
list_is_ordered()
The function that check whether the linked list is in ordered. As we can see, the literation especially skip the first node of the linked list, which is the special case. We can avoid it by checking whether the current and the next node is NULL
, and compare two adjacent nodes at once.
shuffle()
This function shuffle the array into random order.
TODO: Make it computationally better.
quick_sort()
The following non-recursive quicksort is not same as in Optimized QuickSort - C Implementation (Non-recursive), since such the implementation needs doubly linked list, which is contradict to our current node_t
conrfiguration.
pivot
, and assignment the first and the last entry to begin[0]
and end[0]
respectively.left
and larger ones to the right
.begin[]
stores all of the entries for the following iteration, and end[]
records the tail of each begin
element.begin[2]
splice the list, until begin[i] == end[i]
.At this point, since begin[1] ~ begin[4]
cannot be divided anymore, we add them to result
For begin[0]
, we continue the iteration and it will add them to result
in ascending order.
But
left
andright
innode_t
seem useless…
ChenYang YehThu, Mar 7, 2024 14:43 PM
left
and right
in node_t
seem to be useless, maybe we can make the best used of it.
In the figure below, in linux/list.h, the linked list is actually implemented by using doubly circular linked list list_head
.
Both prev
and next
are pointers of the list_head
type, pointing to the type itself. In this, we can easily observe that from the beginning, the Linux kernel does not specify the data type to be stored, significantly increasing the flexibility of linked lists in the design of the Linux kernel.
If we want to store integer data in the linked list, we only need to include the definition of list_head
in list.h and then implement an additional struct.
To make manipulating this linked list more convenient, the Linux kernel provides corresponding APIs for our use.
list_first_entry() / list_last_entry()
Return the first and the last entry of the linked list.
Since the linked list is circular and doubly, we can simply define the macro as
list_for_each()
It use for loop traverses all entries of hlist, but cannot remove nodes.
It would end once pos == head
.
list_for_each_safe()
There have two variables in the for loop.
Thus, it allows us to remove the node during traversal.
INIT_LIST_HEAD()
Initialized the head of the list.
__list_add() / list_add()
Add the node new
between the previous node prev
and the next node next
.
Thus, the function of list_add
is adding the node new
to the head of the list head
.
__list_del() / list_del()
Remove the node entry
from the list.
And list_del()
initializes entry
after it is removed from the list.
list_move()
Move the node entry
to the new linked list head
.
So it just divided into 2 steps.
entry
from the old list.head
.2
: TimsortConsider a following definition of structure
where hlist_head
points to the first element of the hlist.
Since pprev
is an indirect pointer to itself, we may illustrate the graph like this.
and again, there are several relevant APIs we can use
container_of() / list_entry()
Given a member pointer, structure name and the member name, it would return the pointer to the beginning of the structure.
In ISO/IEC 9899 §7.17 3, C standard library provides a macro offsetof()
which returns the offset of the member
.
offsetof(type, member-designator)
which expands to an integer constant expression that has type size_t, the value of which is the offset in bytes, to the structure member, from the beginning of its structure (designated by type).
offsetof()
is defined as
Thus,
means the address to the head of the type
.
What is the meaning of
((TYPE*)0)->MEMBER
? I still cannot understand in stackoverflow.ChenYang YehSat, Mar 9, 2024 19:03 PM
hlist_for_each()
It use for loop traverses all entries of hlist, but cannot remove nodes.
It would end once pos == NULL
.
hlist_for_each_safe()
There have two variables in the for loop.
Thus, it allows us to remove the node during traversal.
Note that {n = pos->next; true}
is a compound statement, which always return the value of last expression. So it assign pos->next
to n
, then return true
.
INIT_HLIST_HEAD()
Initializing hlist_head
to NULL
.
hlist_add_head()
It adds node n
at the beginning of the hlist h
.
It first modifies the indirect pointer pprev
of the first node points to the address of n->next
, and modify next
and pprev
of n
point to the h->first
and &h->first
respectively, then make h->first
point to n
.
hlist_del()
Remove the node n
from the list.
First define next
as a pointer to the next entry of n
and pprev
as an indirect pointer to n
itself.
To remove the node n
, just simply change the pointer pprev
to next
and the pprev
of the next node to pprev
.
3
: Building TreeWe first define the node of the tree TreeNode
and the node order node
.
All of order_node
would be added into hash table by the following node_add()
.
node_add()
This function add the node on
into the hash table heads
.
The idx
is the index of the inorder
array, and hash
is the result of hash function.
After is calculated, on
would be added into the corresponding bucket.
find()
Find the node in hash table, then return its index in inorder array. Return -1
if it doesn't find the node.
dfs()
dfs()
constructs a tree in recursive manner.
Both preorder
and inorder
are array of integers, which are used to store the preorder and inorder array given by user.
pre_low
, pre_high
, in_low
and in_high
are used to record the range of preorder and inorder array in each level of recursion, which is quite important in this implementaiton.
Once it is called, it first check whether both arrays are out of range, if so, directly return NULL
.
Then allocates sizeof(TreeNode)
memories to tn
, use preorder[pre_low]
as the root (or parent) node.
Finally, use find()
to find the idx
of the node in inorder
array, then recursively add left and right childs based on arrays range and idx
.
The rule of preorder traversal in binary tree visits the current (or parent) node first, then traverse left and right childs.
Maybe it can be done iteratively.ChenYang YehSat, Mar 9, 2024 18:23 PM
And in inorder traversal, it visits the left child first, then the parent node and right child.
So, the first few element in preorder
should be the root or parent nodes in the tree. Once we determine the parent node, we can partition inorder array base on idx
of the parent node. Those who has smaller index in inorder array should be assigned to left subtree, and vice versa.
We can verify the result by post ordered tree traversal.
I still not figure out why the range of preorder array should be
ChenYang YehSat, Mar 9, 2024 19:04 PM
cgroups
preorder walk.
4
: LRU CacheLeast Recently Used (LRU) cache is a type of caching mechanism used in computer systems, particularly in the context of managing memory or storage.
The goal of an LRU cache is to keep track of the usage patterns of various items and prioritize the retention of the most recently used items while evicting the least recently used items when the cache reaches its capacity.
LRU cache can also help lower the rate of cache misses in certain scenarios and remain good temporal locality.
*
means LRU Cache.
We first define the structure of LRUCache
and LRUNode
.
lRUCacheCreate()
Create a LRUCache
node, and initializes all of dhead
and all of hlist_head
in the array.
Note that since the definition of LRUCache
is imcompleted type, we cannot use malloc(sizeof(LRUCache))
directly.
Perhaps
malloc(2 * sizeof(int) + sizeof(struct list_head) + capacity * sizeof(struct hlist_head))
is more reasonable. ChenYang YehSat, Mar 9, 2024 23:52 PM
lRUCacheFree()
Free LRUCache
and its relevant LRUNode
.
lRUCacheGet()
move the LRUNode
in the hash table with key
to dhead
of LRUCache
, then return its value
.
It would first search LRUNode
with key
in certain bucket in hash table, if the object is successfully found, move it to the beginning of dhead
and return value
, if not, return -1
.
The hash function is
So at this point, we know that the hash table in LRUCache
is used to store the LRUNode
for finding complexity.
Of course the hash function can be redesigned, or it may not meet SUHA condition when
capacity
is large.
ChenYang YehSun, Mar 10, 2024 01:01 AM
lRUCachePut()
There are two circumstances in lRUCachePut()
.
CacheNode
we want can be found in the hash table of obj
, then move it to the beginning of dhead
and change its value
to value
.CacheNode
we want cannot be found in hash table,
count == capacity
), then we directly remove the last LRUNode
in dhead
, then add it into the beginning of dhead
again and change its value.LRUNode
, and add it to the dhead
and hash table.5
: Find nth BitYou shall explain why such routine exists in the Linux kernel and other applications.
We need to figure out the following macro first.
BITMAP_LAST_WORK_MASK()
The purpose of this macro is to generate a bitmask for a bitmap with a specified number of bits nbits
. Note that since in most Unix and Unix-like systems using LP64 data model, we cannot directly use 0UL
in 32-bit architecture, or it will lead to an error.
We need to declare 32-bit
unsigned integer by our own.
__const_hweight8(w)
This macro calculates the number of set bits (bits set to 1) in an 8-bit binary number, which means the count of bits that are 1 in the given number.
The !!
operator ensures that the resulting calculation yields only 0 or 1, without any other numbers.
!!(00000010)
will return 1 instead of 2.