logo

מבנה נתונים של רשימה מקושרת ב-C++ עם איור

א רשימה מקושרת הוא מעין מבנה נתונים דינמי ליניארי בו אנו משתמשים לאחסון רכיבי נתונים. מערכים הם גם סוג של מבנה נתונים ליניארי שבו פריטי הנתונים מאוחסנים בלוקי זיכרון רציפים.

שלא כמו מערכים, הרשימה המקושרת אינה צריכה לאחסן רכיבי נתונים באזורי זיכרון רציפים או בלוקים.

substring_index ב-sql

א רשימה מקושרת מורכב מאלמנטים המכונים 'צמתים' המחולקים לשני חלקים. הרכיב הראשון הוא החלק שבו אנו מאחסנים את הנתונים בפועל, והשני הוא חלק שבו אנו מאחסנים את המצביע לצומת הבא. סוג זה של מבנה ידוע בשם ' רשימה מקושרת בודדת .'

רשימה מקושרת ב-C++

מדריך זה יעבור לעומק על הרשימה המקושרת בודדת.

מבנה של רשימה מקושרת בודדת מודגם בתרשים שלהלן

מבנה נתונים של רשימה מקושרת ב-C++ עם איור
  • כפי שראינו בחלק שלמעלה, הצומת הראשון של הרשימה המקושרת ידוע כ'ראש', בעוד שהצומת האחרון נקרא 'זנב'. זה בגלל שלא צוינה כתובת זיכרון בצומת האחרון, לצומת הסופי של הרשימה המקושרת יהיה מצביע הבא null.
  • מכיוון שכל צומת כולל מצביע לצומת הבא, אין צורך לשמור על רכיבי נתונים ברשימה המקושרת במיקומים רציפים. הצמתים עשויים להיות מפוזרים ברחבי הזיכרון. מכיוון שלכל צומת יש את הכתובת של זה שאחריו, נוכל לגשת לצמתים מתי שנרצה.
  • אנו יכולים להוסיף ולהסיר במהירות פריטי נתונים מהרשימה המחוברת. כתוצאה מכך, הרשימה המקושרת יכולה להגדיל או להתכווץ באופן דינמי. לרשימה המקושרת אין כמות מקסימלית של פריטי נתונים שהיא יכולה להכיל. כתוצאה מכך, אנו יכולים להוסיף כמה פריטי נתונים שנרצה לרשימה המקושרת כל עוד יש זיכרון RAM זמין.
  • מכיוון שאיננו צריכים לציין מראש כמה פריטים אנו צריכים ברשימה המקושרת, הרשימה המקושרת חוסכת מקום בזיכרון בנוסף להיותה פשוטה להכנסה ולמחיקה. המקום היחיד שבו משתמשת רשימה מקושרת הוא לאחסן את המצביע לצומת הבא, מה שמוסיף עלות מסוימת.

לאחר מכן, נעבור על הפעולות השונות שעשויות להתבצע ברשימה מקושרת.

1) הכנסה

הרשימה המקושרת מורחבת על ידי פעולת ההוספה אליה. למרות שזה נראה פשוט, בהתחשב במבנה הרשימה המקושרת, אנו יודעים שבכל פעם שמתווסף פריט נתונים, עלינו לשנות את המצביעים הבאים של הצמתים הקודמים והבאים של הפריט החדש שהוספנו.

היכן יוכנס פריט הנתונים החדש הוא ההיבט השני שצריך לחשוב עליו.

ישנם שלושה מקומות שבהם ניתן להוסיף פריט נתונים לרשימה המקושרת.

א. החל מהרשימה המקושרת

להלן רשימה מחוברת של המספרים 2->4->6->8->10. הראש המצביע על צומת 2 יצביע כעת על צומת 1, ולמצביע הבא של צומת 1 תהיה כתובת הזיכרון של צומת 2, כפי שמוצג באיור למטה, אם נוסיף צומת 1 חדש כצומת הראשון ברשימה .

מבנה נתונים של רשימה מקושרת ב-C++ עם איור

כתוצאה מכך, הרשימה המקושרת החדשה היא 1->2->4->6->8->10.

ב. אחרי הצומת הנתון

הצהרת if-else java

במקרה זה, ניתן לנו צומת ועלינו להוסיף צומת חדש מאחוריו. הרשימה המקושרת תיראה כך אם צומת f יתווסף לרשימה המקושרת a->b->c->d->e לאחר צומת c:

מבנה נתונים של רשימה מקושרת ב-C++ עם איור

לכן אנו בודקים אם הצומת שצוין קיים בתרשים למעלה. אם הוא קיים, נוצר צומת f חדש. לאחר מכן, נכוון את המצביע הבא של צומת c אל הצומת החדש לגמרי f. המצביע הבא של הצומת f מצביע כעת על הצומת d.

ג. הפריט האחרון של הרשימה המקושרת

במקרה השלישי, צומת חדש נוסף לסוף הרשימה המקושרת. קחו בחשבון את הרשימה המקושרת למטה: a->b->c->d->e, עם תוספת של צומת f בסוף. לאחר הוספת הצומת, הרשימה המקושרת תופיע כך.

מבנה נתונים של רשימה מקושרת ב-C++ עם איור

כתוצאה מכך, אנו בונים צומת חדש f. לאחר מכן מצביע הזנב המוביל ל- null על f, והמצביע הבא של צומת f מצביע על null. בשפת התכנות למטה, יצרנו את כל שלושת סוגי פונקציות ההוספה.

ניתן להכריז על רשימה מקושרת כמבנה או כמחלקה ב-C++. רשימה מקושרת המוצהרת כמבנה היא הצהרה קלאסית בסגנון C. רשימה מקושרת משמשת כמחלקה ב-C++ המודרנית, בעיקר בעת שימוש בספריית התבניות הסטנדרטית.

נעשה שימוש במבנה באפליקציה הבאה כדי להכריז וליצור רשימה מקושרת. חבריו יהיו נתונים ומצביע לרכיב הבא.

תוכנית C++:

 #include using namespace std; struct Node { int data; struct Node *next; }; void push ( struct Node** head, int nodeData ) { struct Node* newNode1 = new Node; newNode1 -&gt; data = nodeData; newNode1 -&gt; next = (*head); (*head) = newNode1; } void insertAfter ( struct Node* prevNode, int nodeData ) { if ( prevNode == NULL ) { cout <data = nodedata; newnode1 -> next = prevNode -&gt; next; prevNode -&gt; next = newNode1; } void append ( struct Node** head, int nodeData ) { struct Node* newNode1 = new Node; struct Node *last = *head; newNode1 -&gt; data = nodeData; newNode1 -&gt; next = NULL; if ( *head == NULL ) { *head = newNode1; return; } while ( last -&gt; next != NULL ) last = last -&gt; next; last -&gt; next = newNode1; return; } void displayList ( struct Node *node ) { while ( node != NULL ) { cout <data <'; node="node" -> next; } if ( node== NULL) cout<next, 55 ); cout << 'final linked list: ' endl; displaylist (head); return 0; } < pre> <p> <strong>Output:</strong> </p> <pre> Final linked list: 35--&gt;25--&gt;55--&gt;15--&gt;45--&gt;null </pre> <h3>2) Deletion</h3> <p>Similar to insertion, deleting a node from a linked list requires many points from which the node might be eliminated. We can remove the linked list&apos;s first, last, or kth node at random. We must correctly update the next pointer and all other linked list pointers in order to maintain the linked list after deletion.</p> <p>In the following C++ implementation, we have two deletion methods: deleting the list&apos;s initial node and deleting the list&apos;s last node. We begin by adding nodes to the head of the list. The list&apos;s contents are then shown following each addition and deletion.</p> <p> <strong>C++ Program:</strong> </p> <pre> #include using namespace std; struct Node { int data; struct Node* next; }; Node* deletingFirstNode ( struct Node* head ) { if ( head == NULL ) return NULL; Node* tempNode = head; head = head -&gt; next; delete tempNode; return head; } Node* removingLastNode ( struct Node* head ) { if ( head == NULL ) return NULL; if ( head -&gt; next == NULL ) { delete head; return NULL; } Node* secondLast = head; while ( secondLast -&gt; next -&gt; next != NULL ) secondLast = secondLast-&gt;next; delete ( secondLast -&gt; next ); secondLast -&gt; next = NULL; return head; } void push ( struct Node** head, int newData ) { struct Node* newNode1 = new Node; newNode1 -&gt; data = newData; newNode1 -&gt; next = ( *head ); ( *head ) = newNode1; } int main() { Node* head = NULL; push ( &amp;head, 25 ); push ( &amp;head, 45 ); push ( &amp;head, 65); push ( &amp;head, 85 ); push ( &amp;head, 95 ); Node* temp; cout &lt;&lt; &apos;Linked list created &apos; &lt; next ) cout <data <'; if ( temp="=" null ) cout << 'null' endl; head="deletingFirstNode" (head); 'linked list after deleting node' < next <data cout<<'null'<<endl; last data 'null'; return 0; } pre> <p> <strong>Output:</strong> </p> <pre> Linked list created 95--&gt;85--&gt;65--&gt;45--&gt;25--&gt;NULL Linked list after deleting head node 85--&gt;65--&gt;45--&gt;25--&gt;NULL Linked list after deleting last node 85--&gt;65--&gt;45--&gt;NULL </pre> <h3>Node Count</h3> <p>While traversing the linked list, the process of counting the number of nodes can be performed. In the preceding approach, we saw that if we needed to insert/delete a node or display the contents of the linked list, we had to traverse the linked list from the beginning.</p> <p>Setting a counter and incrementing as well as we traverse each node will provide us the number of nodes in the linked list.</p> <h3>Differences between Array and Linked list:</h3> <table class="table"> <tr> <th>Array</th> <th>Linked list</th> </tr> <tr> <td>Arrays have a defined size.</td> <td>The size of the linked list is variable.</td> </tr> <tr> <td>Inserting a new element is difficult.</td> <td>Insertion and deletion are simpler.</td> </tr> <tr> <td>Access is permitted at random.</td> <td>No random access is possible.</td> </tr> <tr> <td>Elements are in relatively close or contiguous.</td> <td>The elements are not contiguous.</td> </tr> <tr> <td>No additional room is required for the following pointer.</td> <td>The following pointer requires additional memory.</td> </tr> </table> <h3>Functionality</h3> <p>Since linked lists and arrays are both linear data structures that hold objects, they can be utilised in similar ways for the majority of applications.</p> <p>The following are some examples of linked list applications:</p> <ul> <li>Stacks and queues can be implemented using linked lists.</li> <li>When we need to express graphs as adjacency lists, we can use a linked list to implement them.</li> <li>We can also use a linked list to contain a mathematical polynomial.</li> <li>In the case of hashing, linked lists are employed to implement the buckets.</li> <li>When a programme requires dynamic memory allocation, we can utilize a linked list because linked lists are more efficient in this instance.</li> </ul> <h2>Conclusion</h2> <p>Linked lists are data structures used to hold data elements in a linear but non-contiguous form. A linked list is made up of nodes with two components each. The first component is made up of data, while the second half has a pointer that stores the memory address of the following member of the list.</p> <p>As a sign that the linked list has ended, the last item in the list has its next pointer set to NULL. The Head is the first item on the list. The linked list allows for a variety of actions such as insertion, deletion, traversal, and so on. Linked lists are favoured over arrays for dynamic memory allocation.</p> <p>Linked lists are hard to print or traverse because we can&apos;t access the elements randomly like arrays. When compared to arrays, insertion-deletion procedures are less expensive.</p> <p>In this tutorial, we learned everything there is to know about linear linked lists. Linked lists can also be doubly linked or circular. In our forthcoming tutorials, we will go through these lists in detail.</p> <hr></data></pre></next,></data></data>

2) מחיקה

בדומה להוספה, מחיקת צומת מרשימה מקושרת דורשת נקודות רבות שמהן עשוי הצומת להתבטל. אנו יכולים להסיר את הצומת הראשון, האחרון או ה-kth של הרשימה המקושרת באופן אקראי. עלינו לעדכן נכון את המצביע הבא ואת כל מצביעי הרשימה המקושרת האחרים כדי לשמור על הרשימה המקושרת לאחר המחיקה.

ביישום C++ הבא, יש לנו שתי שיטות מחיקה: מחיקת הצומת הראשוני של הרשימה ומחיקת הצומת האחרון של הרשימה. אנו מתחילים בהוספת צמתים לראש הרשימה. תוכן הרשימה מוצג לאחר כל הוספה ומחיקה.

להתחבר ל-Java של מסד נתונים

תוכנית C++:

 #include using namespace std; struct Node { int data; struct Node* next; }; Node* deletingFirstNode ( struct Node* head ) { if ( head == NULL ) return NULL; Node* tempNode = head; head = head -&gt; next; delete tempNode; return head; } Node* removingLastNode ( struct Node* head ) { if ( head == NULL ) return NULL; if ( head -&gt; next == NULL ) { delete head; return NULL; } Node* secondLast = head; while ( secondLast -&gt; next -&gt; next != NULL ) secondLast = secondLast-&gt;next; delete ( secondLast -&gt; next ); secondLast -&gt; next = NULL; return head; } void push ( struct Node** head, int newData ) { struct Node* newNode1 = new Node; newNode1 -&gt; data = newData; newNode1 -&gt; next = ( *head ); ( *head ) = newNode1; } int main() { Node* head = NULL; push ( &amp;head, 25 ); push ( &amp;head, 45 ); push ( &amp;head, 65); push ( &amp;head, 85 ); push ( &amp;head, 95 ); Node* temp; cout &lt;&lt; &apos;Linked list created &apos; &lt; next ) cout <data <\'; if ( temp="=" null ) cout << \'null\' endl; head="deletingFirstNode" (head); \'linked list after deleting node\' < next <data cout<<\'null\'<<endl; last data \'null\'; return 0; } pre> <p> <strong>Output:</strong> </p> <pre> Linked list created 95--&gt;85--&gt;65--&gt;45--&gt;25--&gt;NULL Linked list after deleting head node 85--&gt;65--&gt;45--&gt;25--&gt;NULL Linked list after deleting last node 85--&gt;65--&gt;45--&gt;NULL </pre> <h3>Node Count</h3> <p>While traversing the linked list, the process of counting the number of nodes can be performed. In the preceding approach, we saw that if we needed to insert/delete a node or display the contents of the linked list, we had to traverse the linked list from the beginning.</p> <p>Setting a counter and incrementing as well as we traverse each node will provide us the number of nodes in the linked list.</p> <h3>Differences between Array and Linked list:</h3> <table class="table"> <tr> <th>Array</th> <th>Linked list</th> </tr> <tr> <td>Arrays have a defined size.</td> <td>The size of the linked list is variable.</td> </tr> <tr> <td>Inserting a new element is difficult.</td> <td>Insertion and deletion are simpler.</td> </tr> <tr> <td>Access is permitted at random.</td> <td>No random access is possible.</td> </tr> <tr> <td>Elements are in relatively close or contiguous.</td> <td>The elements are not contiguous.</td> </tr> <tr> <td>No additional room is required for the following pointer.</td> <td>The following pointer requires additional memory.</td> </tr> </table> <h3>Functionality</h3> <p>Since linked lists and arrays are both linear data structures that hold objects, they can be utilised in similar ways for the majority of applications.</p> <p>The following are some examples of linked list applications:</p> <ul> <li>Stacks and queues can be implemented using linked lists.</li> <li>When we need to express graphs as adjacency lists, we can use a linked list to implement them.</li> <li>We can also use a linked list to contain a mathematical polynomial.</li> <li>In the case of hashing, linked lists are employed to implement the buckets.</li> <li>When a programme requires dynamic memory allocation, we can utilize a linked list because linked lists are more efficient in this instance.</li> </ul> <h2>Conclusion</h2> <p>Linked lists are data structures used to hold data elements in a linear but non-contiguous form. A linked list is made up of nodes with two components each. The first component is made up of data, while the second half has a pointer that stores the memory address of the following member of the list.</p> <p>As a sign that the linked list has ended, the last item in the list has its next pointer set to NULL. The Head is the first item on the list. The linked list allows for a variety of actions such as insertion, deletion, traversal, and so on. Linked lists are favoured over arrays for dynamic memory allocation.</p> <p>Linked lists are hard to print or traverse because we can&apos;t access the elements randomly like arrays. When compared to arrays, insertion-deletion procedures are less expensive.</p> <p>In this tutorial, we learned everything there is to know about linear linked lists. Linked lists can also be doubly linked or circular. In our forthcoming tutorials, we will go through these lists in detail.</p> <hr></data>

ספירת צמתים

בזמן מעבר ברשימה המקושרת, ניתן לבצע את תהליך ספירת מספר הצמתים. בגישה הקודמת ראינו שאם עלינו להכניס/למחוק צומת או להציג את תוכן הרשימה המקושרת, עלינו לעבור את הרשימה המקושרת מההתחלה.

הגדרת מונה והגדלה, כמו גם שאנו חוצים כל צומת, יספקו לנו את מספר הצמתים ברשימה המקושרת.

הבדלים בין מערך לרשימה מקושרת:

מַעֲרָך רשימה מקושרת
למערכים יש גודל מוגדר. גודל הרשימה המקושרת משתנה.
קשה להכניס אלמנט חדש. הכנסה ומחיקה פשוטות יותר.
הגישה מותרת באופן אקראי. אין גישה אקראית אפשרית.
אלמנטים קרובים יחסית או רציפים. האלמנטים אינם רציפים.
אין צורך בחדר נוסף עבור המצביע הבא. המצביע הבא דורש זיכרון נוסף.

פונקציונליות

מכיוון שרשימות ומערכים מקושרים הם שניהם מבני נתונים ליניאריים המכילים אובייקטים, ניתן להשתמש בהם בדרכים דומות עבור רוב היישומים.

להלן כמה דוגמאות ליישומי רשימה מקושרת:

עיר בארצות הברית
  • ניתן ליישם ערימות ותורים באמצעות רשימות מקושרות.
  • כאשר אנו צריכים לבטא גרפים כרשימות סמיכות, אנו יכולים להשתמש ברשימה מקושרת כדי ליישם אותם.
  • אנו יכולים גם להשתמש ברשימה מקושרת כדי להכיל פולינום מתמטי.
  • במקרה של hashing, רשימות מקושרות משמשות ליישם את הדליים.
  • כאשר תוכנית דורשת הקצאת זיכרון דינמית, אנו יכולים להשתמש ברשימה מקושרת מכיוון שרשימות מקושרות יעילות יותר במקרה זה.

סיכום

רשימות מקושרות הן מבני נתונים המשמשים להחזיק רכיבי נתונים בצורה ליניארית אך לא רציפה. רשימה מקושרת מורכבת מצמתים עם שני רכיבים כל אחד. הרכיב הראשון מורכב מנתונים, בעוד שבמחצית השנייה יש מצביע המאחסן את כתובת הזיכרון של החבר הבא ברשימה.

כסימן לכך שהרשימה המקושרת הסתיימה, לפריט האחרון ברשימה המצביע הבא מוגדר ל-NULL. הראש הוא הפריט הראשון ברשימה. הרשימה המקושרת מאפשרת מגוון פעולות כמו הכנסה, מחיקה, מעבר וכו'. רשימות מקושרות מועדפות על פני מערכים להקצאת זיכרון דינמית.

קשה להדפיס או לעבור רשימות מקושרות מכיוון שאיננו יכולים לגשת לאלמנטים באופן אקראי כמו מערכים. בהשוואה למערכים, הליכי הכנסה-מחיקה זולים יותר.

במדריך זה, למדנו כל מה שצריך לדעת על רשימות מקושרות ליניאריות. רשימות מקושרות יכולות להיות מקושרות כפולות או מעגליות. בהדרכות הקרובות שלנו, נעבור על הרשימות הללו בפירוט.