
Лекція 7. Зв’язані списки з подвійним зв'язком
7.1. Структура списку з подвійним зв’язком
7.2. Операції над списком з подвійним зв’язком
7.3. Реалізація списку з подвійним зв’язком на мові C#
7.1. Структура списку з подвійним зв’язком
Кожен елемент в списку з подвійним зв'язком має покажчик на наступний елемент списку і покажчик на попередній елемент списку. Рис.1 ілюструє характер зв'язків в такому списку. Список, який замість одного має два зв'язки, відрізняється двома основними перевагами.
По-перше, список можна прочитати в обох напрямах. Це не лише спрощує сортування списку, але також дозволяє користувачам бази даних переглядати дані в обох напрямах.
По-друге, і прямий і зворотний зв'язок дозволяють прочитати список повністю і тому при порушенні одного із зв'язків список може бути відновлений по іншому зв'язку. Цю властивість має сенс використовувати при відмовах устаткування, що призводять до порушення списку.
Рис. 1. Список з подвійним зв'язком.
Http://professorweb.Ru/my/csharp/charp_theory/level12/12_8.Php
7.2. Операції над списком з подвійним зв’язком
Для списку з подвійним зв'язком передбачаються три основні операції:
- вставка нового першого елементу;
- вставка нового середнього елементу;
- вставка нового останнього елементу.
Крім цього, для списку з подвійним зв’язком є операції видалення елементу, обхід (переміщення) списку у прямому і зворотному напрямах.
7.3. Реалізація списку з подвійним зв’язком на мові c#
Клас LinkedList<T> реалізує колекцію у вигляді універсального списку з подвійним зв’язком (двозв’язного списку). У цьому класі реалізуються інтерфейси ICollection, ICollection<T>, IEnumerable, IEnumerable<T>, ISerializable і IDeserializationCallback.
Таблиця 1. Конструктори клас у LinkedList<T>
Ім’я |
Опис |
LinkedList<T>() |
Створюється порожній зв'язний список |
LinkedList<T>(IEnumerable<T>) |
Створюється список, що ініціалізується елементами з колекції collection. |
Таблиця 2. Властивості класу LinkedList<T>
Ім’я |
Опис |
Count |
Отримує число вузлів, яке насправді зберігається в LinkedList<T> |
First |
Отримує перший вузол об'єкту LinkedList<T>. |
Last |
Отримує останній вузол об'єкту LinkedList<T>. |
Як і в більшості інших реалізацій зв'язних списків, в класі LinkedList<T> містяться значення, що зберігаються у вузлах списку, де знаходяться також посилання на попередні і наступні елементи списку. Ці вузли є об'єктами класу LinkedListNode<T>.
У класі LinkedListNode<T> реалізуються чотири наступні властивості.
Таблиця 3 Властивості класу LinkedListNode<T>.
Ім’я |
Опис |
Next |
Посилання на наступний вузол списку або null (якщо вузол відсутній) |
Previous |
Посилання на попередній вузол списку або null (якщо вузол відсутній) |
List |
Отримання посилання на сам список |
Value |
Встановлення чи отримання значення, що знаходиться у вузлі списку |
За допомогою властивостей Next і Previous виконують посилання на попередній і наступний вузли списку відповідно, що дає можливість обходити список в обох напрямах.
Таблиця 4. Основні методи класу LinkedList<T>.
Ім'я |
Опис |
AddAfter(LinkedListNode<T>, LinkedListNode<T>) |
Додає заданий новий вузол після заданого існуючого вузла в LinkedList<T>. |
AddAfter(LinkedListNode<T>, T) |
Додає новий вузол, що містить задане значення, після заданого існуючого вузла в LinkedList<T>. |
AddBefore(LinkedListNode<T>, LinkedListNode<T>) |
Додає заданий новий вузол перед заданим існуючим вузлом в LinkedList<T>. |
AddBefore(LinkedListNode<T>, T) |
Додає новий вузол, що містить задане значення, перед заданим існуючим вузлом в LinkedList<T>. |
AddFirst(T) |
Додає новий вузол, що містить задане значення, в початок LinkedList<T>. |
AddFirst(LinkedListNode<T>) |
Додає заданий новий вузол в початок LinkedList<T>. |
AddLast(T) |
Додає новий вузол, що містить задане значення, в кінець LinkedList<T>. |
AddLast(LinkedListNode<T>) |
Додає заданий новий вузол в кінець об'єкту LinkedList<T>. |
Clear |
Видаляє усі вузли з LinkedList<T>. |
Contains |
Визначає, чи належить значення об'єкту LinkedList<T>. |
CopyTo |
Копіює цілий масив LinkedList<T> у сумісний одновимірний масив Array, починаючи із заданого індексу цільового масиву. |
Equals(Object) |
Визначає, чи рівний заданий об'єкт поточному об'єкту. (Успадковано від Object.) |
Finalize |
Дозволяє об'єкту спробувати звільнити ресурси і виконати інші операції очищення, перш ніж об'єкт буде знищено в процесі збирання сміття. (Успадковано від Object.) |
Find |
Знаходить перший вузол, що містить вказане значення. |
FindLast |
Знаходить останній вузол, що містить вказане значення. |
GetEnumerator |
Повертає нумератор, що здійснює перебір елементів LinkedList<T>. |
GetHashCode |
Грає роль хеш-функції для певного типу. (Успадковано від Object.) |
GetType |
Повертає об'єкт Type для поточного екземпляра. (Успадковано від Object.) |
Remove(T) |
Видаляє перше входження заданого значення з LinkedList<T> |
Remove(LinkedListNode<T>) |
Видаляє заданий вузол з об'єкту LinkedList<T>. |
RemoveFirst |
Видаляє вузол на початку LinkedList<T>. |
RemoveLast |
Видаляє вузол у кінці LinkedList<T>. |
ToString |
Повертає рядок, що представляє поточний об'єкт. (Успадковано від Object.) |
Приклад 1 . Демонструє роботу з LinkedList<T>
https://msdn.microsoft.com/ru-ru/library/he2s3bh7(v=vs.110).aspx
using System;
using System.Text;
using System.Collections.Generic;
public class Example
{
public static void Main()
{
// Create the link list.
string[] words =
{ "the", "fox", "jumped", "over", "the", "dog" };
LinkedList<string> sentence = new LinkedList<string>(words);
Display(sentence, "The linked list values:");
Console.WriteLine("sentence.Contains(\"jumped\") = {0}",
sentence.Contains("jumped"));
// Add the word 'today' to the beginning of the linked list.
sentence.AddFirst("today");
Display(sentence, "Test 1: Add 'today' to beginning of the list:");
// Move the first node to be the last node.
LinkedListNode<string> mark1 = sentence.First;
sentence.RemoveFirst();
sentence.AddLast(mark1);
Display(sentence, "Test 2: Move first node to be last node:");
// Change the last node be 'yesterday'.
sentence.RemoveLast();
sentence.AddLast("yesterday");
Display(sentence, "Test 3: Change the last node to 'yesterday':");
// Move the last node to be the first node.
mark1 = sentence.Last;
sentence.RemoveLast();
sentence.AddFirst(mark1);
Display(sentence, "Test 4: Move last node to be first node:");
// Indicate, by using parentheisis, the last occurence of 'the'.
sentence.RemoveFirst();
LinkedListNode<string> current = sentence.FindLast("the");
IndicateNode(current, "Test 5: Indicate last occurence of 'the':");
// Add 'lazy' and 'old' after 'the' (the LinkedListNode named current).
sentence.AddAfter(current, "old");
sentence.AddAfter(current, "lazy");
IndicateNode(current, "Test 6: Add 'lazy' and 'old' after 'the':");
// Indicate 'fox' node.
current = sentence.Find("fox");
IndicateNode(current, "Test 7: Indicate the 'fox' node:");
// Add 'quick' and 'brown' before 'fox':
sentence.AddBefore(current, "quick");
sentence.AddBefore(current, "brown");
IndicateNode(current, "Test 8: Add 'quick' and 'brown' before 'fox':");
// Keep a reference to the current node, 'fox',
// and to the previous node in the list. Indicate the 'dog' node.
mark1 = current;
LinkedListNode<string> mark2 = current.Previous;
current = sentence.Find("dog");
IndicateNode(current, "Test 9: Indicate the 'dog' node:");
// The AddBefore method throws an InvalidOperationException
// if you try to add a node that already belongs to a list.
Console.WriteLine("Test 10: Throw exception by adding node (fox) already in the list:");
try
{
sentence.AddBefore(current, mark1);
}
catch (InvalidOperationException ex)
{
Console.WriteLine("Exception message: {0}", ex.Message);
}
Console.WriteLine();
// Remove the node referred to by mark1, and then add it
// before the node referred to by current.
// Indicate the node referred to by current.
sentence.Remove(mark1);
sentence.AddBefore(current, mark1);
IndicateNode(current, "Test 11: Move a referenced node (fox) before the current node (dog):");
// Remove the node referred to by current.
sentence.Remove(current);
IndicateNode(current, "Test 12: Remove current node (dog) and attempt to indicate it:");
// Add the node after the node referred to by mark2.
sentence.AddAfter(mark2, current);
IndicateNode(current, "Test 13: Add node removed in test 11 after a referenced node (brown):");
// The Remove method finds and removes the
// first node that that has the specified value.
sentence.Remove("old");
Display(sentence, "Test 14: Remove node that has the value 'old':");
// When the linked list is cast to ICollection(Of String),
// the Add method adds a node to the end of the list.
sentence.RemoveLast();
ICollection<string> icoll = sentence;
icoll.Add("rhinoceros");
Display(sentence, "Test 15: Remove last node, cast to ICollection, and add 'rhinoceros':");
Console.WriteLine("Test 16: Copy the list to an array:");
// Create an array with the same number of
// elements as the inked list.
string[] sArray = new string[sentence.Count];
sentence.CopyTo(sArray, 0);
foreach (string s in sArray)
{
Console.WriteLine(s);
}
// Release all the nodes.
sentence.Clear();
Console.WriteLine();
Console.WriteLine("Test 17: Clear linked list. Contains 'jumped' = {0}",
sentence.Contains("jumped"));
Console.ReadLine();
}
private static void Display(LinkedList<string> words, string test)
{
Console.WriteLine(test);
foreach (string word in words)
{
Console.Write(word + " ");
}
Console.WriteLine();
Console.WriteLine();
}
private static void IndicateNode(LinkedListNode<string> node, string test)
{
Console.WriteLine(test);
if (node.List == null)
{
Console.WriteLine("Node '{0}' is not in the list.\n",
node.Value);
return;
}
StringBuilder result = new StringBuilder("(" + node.Value + ")");
LinkedListNode<string> nodeP = node.Previous;
while (nodeP != null)
{
result.Insert(0, nodeP.Value + " ");
nodeP = nodeP.Previous;
}
node = node.Next;
while (node != null)
{
result.Append(" " + node.Value);
node = node.Next;
}
Console.WriteLine(result);
Console.WriteLine();
}
}
Приклад 2. У прикладі створюється список Прізвищ та виконується перегляд списку у прямому і зворотному напрямах.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Lab_7
{
class Program
{
static void Main(string[] args)
{
// Створимо зв'язний список
LinkedList<string> link = new LinkedList<string>();
//Додамо декілька елементів
link.AddFirst("Іванов");
link.AddFirst("Петров");
link.AddFirst("Сидоров");
link.AddFirst("Коваленко");
// Відобразимо елементи в прямому напрямі
LinkedListNode<string> node;
Console.WriteLine("Елементи списку у прямому напрямку: ");
for (node = link.First; node != null; node = node.Next)
Console.Write(node.Value + "\t");
// Відобразимо елементи у зворотному напрямі
Console.WriteLine("\n\nЕлементи списку у зворотньому напрямку: ");
for (node = link.Last; node != null; node = node.Previous)
Console.Write(node.Value + "\t");
Console.ReadKey();
}
}
}
Приклад 3. Форма для роботи зі списком
У прикладі створюється клас Student і зв’язаний список об’єктів класу. Керування роботою зі списком виконується за допомогою Windows Forms. Програма дозволяє проглядати вузли списку у прямому і зворотному напрямах, додавати, змінювати та видаляти вузли списку.
файл Student.cs – код класу
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace WinForm_1
{
class Student
{
//Властивості
public string FistName { get; set; } //ім'я
public string LastName{ get; set; } //прізвище
public string Role { get; set; } // роль
public int Course { get; private set; }
public int Rating { get; set; }
public Student(string f, string n, int cource, int rating)
{
//конструктор з параметрами
//конструктор з параметрами. Ініціалізація полів і властивостей
LastName = f;
FistName = n;
Course = cource;
Rating = rating;
}
public Student()
{ }
public string GetRole(int course)
{
if (course <= 4)
Role = "бакалавр";
else
Role = "магістр";
return Role;
}
public void St_Rating(int student_rating)
{
if (student_rating >= 82)
Console.WriteLine("Привіт відмінникам");
else
if (student_rating <= 45)
Console.WriteLine("Перездача! Треба краще вчитися!");
else
Console.WriteLine("Можна вчитися ще краще!");
}
}
}
Форма
Код форми
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WinForm_18
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
LinkedList<Student> list_Student = new LinkedList<Student>(); //список
LinkedListNode<Student> node; // вузол списку
private void Form1_Load(object sender, EventArgs e)
{
//початок роботи форми
CreateListStudent();
}
public void CreateListStudent()
{
//значення поточного вузла виводимо у поля форми
list_Student.AddFirst(new Student("Батиренко", "Артем", 4, 90));
list_Student.AddFirst(new Student("Сахно", "Name",4,85));
list_Student.AddFirst(new Student("Сусла", "Name2",4,56));
list_Student.AddFirst(new Student("Ярославський","Name",4,56));
node = list_Student.First;
SetNode();
}
private void SetNode()
{ //виведення на форму першого елемента списку
txtLatName.Text = node.Value.LastName;
txtFirsName.Text = node.Value.FistName;
txtCource.Text = node.Value.Course.ToString();
txtRating.Text = node.Value.Rating.ToString();
}
private void btmRole_Click(object sender, EventArgs e)
{
Student st = new Student();
int course = int.Parse(txtCource.Text);
txtRole.Text = st.GetRole(course);
}
private void btnFirst_Click(object sender, EventArgs e)
{
//First
node = list_Student.First;
SetNode();
}
private void btnLast_Click(object sender, EventArgs e)
{
//Last
node = list_Student.Last;
SetNode();
}
private void btnPrev_Click(object sender, EventArgs e)
{
//Prew
if (node != list_Student.First)
{
node = node.Previous;
SetNode();
}
else { //початок списку
node = list_Student.First;
SetNode();
}
}
private void btnNext_Click(object sender, EventArgs e)
{
if (node != list_Student.Last)
{
node = node.Next;
SetNode();
}
else
{ //кінець списку
node = list_Student.Last;
SetNode();
}
}
private void btnAdd_Click(object sender, EventArgs e)
{
//ADD to List - Clear textBoxs
ClearForm();
}
private void ClearForm()
{
txtLatName.Text = "";
txtFirsName.Text ="";
txtCource.Text = "";
txtRating.Text = "";
txtRole.Text = "";
}
private void btnSave_Click(object sender, EventArgs e)
{
// Add to List
list_Student.AddFirst(new Student(txtLatName.Text, txtFirsName.Text, int.Parse(txtCource.Text), int.Parse(txtRating.Text)));
}
private void btnClear_Click(object sender, EventArgs e)
{
ClearForm();
}
private void btnDel_Click(object sender, EventArgs e)
{
//Delete from List
list_Student.Remove(node);
ClearForm();
}
private void button1_Click(object sender, EventArgs e)
{
this.Close();
}
}
}
Контрольні запитання і завдання для самостійної роботи
1. Поясніть структуру списку з подвійним зв’язком.
2. Які операції потрібно реалізувати над списком з подвійним зв’язком?
3. Яка перевага використання списку з подвійним зв’язком перед звичайним лінійним списком?
4. Який клас мови С# реалізує список з подвійним зв’язком?
5. Чи можна вставити елемент у середину списку з подвійним зв’язком? Чи збережеться при цьому порядок елементів?
6. Чи можна видаляти елементи із середини списку?
7. Як створити порожній список з подвійним зв’язком на мові C#?
8. Як створити список подвійним зв’язком для роботи з об’єктами класу Person на мові C#?
9. Як отримати значення, що знаходиться у вузлі списку LinkedListNode?
10. Яке призначення класу LinkedListNode<T>?
11. Як додати на мові C# новий вузол у початок списку LinkedList?
12. Реалізувати на мові C# роботу зі списком з подвійним зв’язком для запису адрес