Привет, я пытаюсь попрактиковаться в использовании связанных списков.
Я определил класс объекта под названием Student:
public class Student
{
protected string Student_Name;
protected int Student_ID;
protected int Student_Mark;
protected char Student_Grade;
public Student() // default Constructor
{
Student_Name = " ";
Student_ID = 0;
Student_Mark = 0;
Student_Grade = ' ';
}
public Student(string Sname, int Sid, int Smark, char Sgrade) // Constructor
{
int len = sname.Length;
Student_Name = sname.Substring(0, len);
//Student_Name = sname.Substring(0, sname.Length);
Student_ID = Sid;
Student_Mark = Smark;
Student_Grade = Sgrade;
}
}
а затем класс Node:
public class S_Node
{
public Student Element;
public S_Node Link;
public S_Node()
{
Element = new Student();
Link = null;
}
public Node(Student theElement)
{
Element = theElement;
Link = null;
}
}
и LinkedList:
public class S_LinkedList
{
protected S_Node header;
protected S_Node tail;
public S_LinkedList()
{
header = new S_Node();
Tail = new S_Node();
header.Link = Tail;
}
// METHODS which i don't know how to do it (never use linkedlist before)
}
Мне нужно организовать эти данные, используя «тип структуры данных связанного списка».
Содержат все методы связанного списка как Добавление узлов в список, как я узнал -> (Вставить), Удаление узлов из списка, как я узнал -> ((Удалить), Перемещение по спискам, которые я изучил - -> ((PrintList), Нахождение узла в списке, как я узнал -> ((Find, FindPrevious) проблема, которую я самообучаю, и я попытался поискать в сети и прочитать больше из глупого C#, который была катастрофа. Я сделал слишком много, и мне так грустно, что я не знаю, как это закончить.
Я очень стараюсь использовать эти классы для написания исполняемой программы и ее тестирования.
Если вы не хотите помогать в завершении этой программы (надеюсь, что нет), по крайней мере, покажите мне несколько реальных примеров или идей, в конце концов, я выродок-самоучка :-)
Я попытался отредактировать вопрос, чтобы придать ему больше смысла, но нижний абзац (начинающийся с «Содержать все») слишком сбивает с толку.





На самом деле я не вижу причин писать свой собственный связанный список на C# (кроме изучения того, как он работает), поскольку .NET уже содержит общий класс LinkedList.
вы правы, но я хочу попробовать, я даже сейчас пытаюсь создать свой собственный математический класс вместо булита в математическом классе, который включает GCD и LCM, мне очень нравится это делать (я назову это MyMath :-)) . Не могли бы вы помочь
Ваш вопрос, как я его читал, слишком расплывчатый. Я бы начал с поиска «связанных списков» или книги о «структурах данных». Когда вы столкнетесь с конкретной проблемой, задайте ее здесь, и я уверен, что кто-то вам поможет.
ну, у меня проблема, и я сделал большую часть, и мне действительно надоело использовать google, я нашел тысячи статей, и каждая из них отличается от другой, если вы знаете где-то пример, похожий на это, пожалуйста, опубликуйте .
Я сделаю для тебя одну! Вам нужно нарисовать диаграммы с каждым узлом в виде прямоугольника и решить, какой код нужно использовать для изменения списка для каждой операции. Посмотрите это для вдохновения:
http://en.wikipedia.org/wiki/Linked_list
На приведенных там диаграммах не показан основной класс списка в виде прямоугольника, который у вас должен быть, с двумя выходящими из него стрелками для заголовка и хвоста.
Нарисуйте себе несколько диаграмм для двух случаев в методе Insert, чтобы понять, что происходит. Одна диаграмма для случаев, когда в списке ничего нет, а заголовок пуст, а другая диаграмма для случаев, когда что-то уже есть в списке. Затем оттуда отработайте остальные операции.
public class S_LinkedList {
protected S_Node header = null;
protected S_Node tail = null;
public S_LinkedList()
{
}
// METHODS which i don't know how to do it (never use linkedlist before)
void Insert(Student s)
{
if ( header == null )
{
header = new S_Node(s);
tail = header;
}
else
{
tail.Link = new S_Node(s);
tail = tail.Link;
}
}
}
Я пробовал что-нибудь похожее на этого Скотта, но это не работает, почему ?! public void Insert (Object newItem, Object after) {Node current = new Node (); Узел newNode = новый узел (newItem); current = Найти (после); newNode.Link = current.Link; current.Link = newNode; }
Не уверена. Такой код выглядит правильным. Помните, что если вставка находится в конце списка, тогда tail потребуется обновить, чтобы ссылаться на новый узел. Вы должны сказать, что именно вы видите, что заставляет вас думать, что это не работает. Он компилируется? Работает ли он без исключения?
Вам не хватает следующих операций:
Добавлять:
установить связь хвостового узла как добавленный узел и установить хвост как новый узел.
Удалить / Удалить:
это немного сложно, если у вас нет двусвязного списка, но с односвязным списком, пройдитесь по списку от головы до тех пор, пока вы не найдете требуемый узел, сохраняя предыдущий узел в отдельной переменной. Когда вы найдете удаляемый узел, установите связь предыдущего узла с этим узлом. Оптимизация может заключаться в проверке того, что это не та ссылка, которую вы ищете. В качестве альтернативы сделайте его двусвязным списком, и вам не нужно отслеживать предыдущий узел.
Находить:
Просматривайте список от узла к узлу, пока не найдете тот, который ищете.
прочтите это статья в Википедии для получения дополнительной информации.
Посмотрите на это...
Хотя, если вы действительно хотите узнать, как это сделать, вы должны написать это на c или C++, по крайней мере, тогда вы будете делать что-то полезное ...
Понятие связанных списков не очень сложно понять. С другой стороны, реализация ... может быть немного сложной.
Я также понимаю ваше разочарование от попыток найти информацию об этом в Интернете. Я был в вашей лодке раньше, и все меняется от сайта к сайту. Возможно, вы действительно захотите вложить деньги в книгу о структурах данных, поскольку я думаю, что информация, которую вы найдете, будет намного более ясной и полезной, чем большая часть информации, которую вы найдете в дикой природе.
Реализовать связанный список на Java / C# будет намного проще, если вы никогда раньше не использовали ll. Однако, как только вы лучше это почувствуете, вы значительно лучше поймете все, создавая их на C / C++.
Из приведенного выше кода вам будет лучше думать о каждом S_Node как о обычном узле, содержащем объект Student, а не думать о нем как о узле Student (надеюсь, что это имеет смысл). Те же правила применяются для вашего класса S_LinkedList. Связанный список - это режим списка узлов. Эти узлы содержат объекты Student.
Надеюсь это поможет.
Попробуйте это в качестве своего студенческого класса.
public class Student
{
protected string Name;
protected int ID;
protected int Mark;
protected char Grade;
public Student() // default Constructor
{
Name = "";
ID = 0;
Mark = 0;
Grade = '';
}
public Student(string Name, int ID, int Mark, char Grade) // Constructor
{
this.Name = Name;
this.ID = ID;
this.Mark = Mark;
this.Grade = Grade;
}
}
Я все еще не могу получить исполняемую программу
the head of the list.
( item1
Element: student1
Next ------------> ( item2
) Element: student2
Next ------------> ( item3
) Element: student3
Next: null
)
the tail of the list.
Прежде всего, чтобы вы могли написать класс StudentList, вам нужно сначала написать клиентский код. Код клиента - это код, который использует ваш список студентов. Кроме того, не пишите что-то одно за раз и выбрасывайте. Вместо этого напишите целую кучу [тестовых] примеров, в которых проверяются различные способы взаимодействия необходимость с StudentList. Пишите и исключительные случаи. Но не поддавайтесь соблазну написать классный швейцарский армейский нож, который делает все просто потому, что может. Напишите минимальный объем кода, который выполняет свою работу.
То, как вам нужно использовать класс, будет во многом определять его построение. В этом суть TDD или Test Driven Design.
Ваша самая большая проблема, которую я вижу, заключается в том, что вы не знаете, как вы хотите использовать класс. Итак, давайте сделаем это в первую очередь.
// create a list of students and print them back out.
StudentList list = new StudentList();
list.Add( new Student("Bob", 1234, 2, 'A') );
list.Add( new Student("Mary", 2345, 4, 'C') );
foreach( Student student in list)
{
Console.WriteLine(student.Name);
}
Я добавляю студентов в список и распечатываю их.
Мне не нужно, чтобы мой клиентский код заглядывал внутрь StudentList. Поэтому StudentList скрывает, как он реализует связанный список. Напишем основы StudentList.
public class StudentList
{
private ListNode _firstElement; // always need to keep track of the head.
private class ListNode
{
public Student Element { get; set; }
public ListNode Next { get; set; }
}
public void Add(Student student) { /* TODO */ }
}
StudentList довольно простой. Внутренне он отслеживает первые или головные узлы. Очевидно, что всегда необходимо отслеживать первый узел.
Вы также можете задаться вопросом, почему ListNode объявлен внутри StudentList. Что происходит, так это то, что класс ListNode доступен только для класса StudentList. Это сделано потому, что StudentList не хочет раскрывать детали своей внутренней реализации, потому что он контролирует весь доступ к списку. StudentList никогда не раскрывает, как реализован список. Скрытие реализации - важная объектно-ориентированная концепция.
Если бы мы позволили клиентскому коду напрямую манипулировать списком, не было бы смысла ставить StudentList на первое место.
Давайте продолжим и реализуем операцию Add ().
public void Add(Student student)
{
if (student == null)
throw new ArgumentNullException("student");
// create the new element
ListNode insert = new ListNode() { Element = student };
if ( _firstElement == null )
{
_firstElement = insert;
return;
}
ListNode current = _firstElement;
while (current.Next != null)
{
current = current.Next;
}
current.Next = insert;
}
Операция добавления должна найти последний элемент в списке, а затем поместить новый ListNode в конец. Хотя не очень эффективно. В настоящее время это O (N), и добавление будет медленнее по мере того, как список становится длиннее.
Давайте немного оптимизируем это для вставок и перепишем метод Add. Чтобы ускорить добавление, все, что нам нужно сделать, это сделать так, чтобы StudentList отслеживал последний элемент в списке.
private ListNode _lastElement; // keep track of the last element: Adding is O(1) instead of O(n)
public void Add(Student student)
{
if ( student == null )
throw new ArgumentNullException("student");
// create the new element
ListNode insert = new ListNode() { Element = student };
if (_firstElement == null)
{
_firstElement = insert;
_lastElement = insert;
return;
}
// fix up Next reference
ListNode last = _lastElement;
last.Next = insert;
_lastElement = insert;
}
Теперь, когда мы добавляем, мы не повторяем. Нам просто нужно отслеживать ссылки на голову и хвост.
Далее: цикл foreach. StudentList - это коллекция, и, будучи коллекцией, мы хотим перечислить ее и использовать C# foreach. Компилятор C# не может волшебным образом выполнять итерацию. Чтобы использовать цикл foreach, нам необходимо предоставить компилятору перечислитель, который будет использоваться, даже если код, который мы пишем, явно не использует перечислитель.
Но сначала давайте еще раз посмотрим, как мы перебираем связанный список.
// don't add this to StudentList
void IterateOverList( ListNode current )
{
while (current != null)
{
current = current.Next;
}
}
Хорошо. так что давайте подключимся к циклу C# foreach и вернем перечислитель. Для этого мы изменяем StudentList для реализации IEnumerable. Это немного продвинулось, но вы сможете понять, что происходит.
// StudentList now implements IEnumerable<Student>
public class StudentList : IEnumerable<Student>
{
// previous code omitted
#region IEnumerable<Student> Members
public IEnumerator<Student> GetEnumerator()
{
ListNode current = _firstElement;
while (current != null)
{
yield return current.Element;
current = current.Next;
}
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
Вы должны увидеть там итерацию связанного списка. Не поддавайтесь ключевому слову yield. Все, что делает yield, - это возвращает текущего студента обратно в цикл foreach. Перечислитель перестает возвращать студентов, когда доходит до конца связанного списка.
Вот и все! Код работает так, как мы хотим.
* Это ни в коем случае не единственный способ реализовать список. Я решил поместить логику списка в StudentList и оставить ListNode очень простым. Но код делает только то, что нужно моему самому первому модульному тесту, и не более того. Вы можете сделать больше оптимизаций, и есть другие способы составления списка.
Забегая вперед: вам нужно сначала создать [модульные] тесты того, что должен делать ваш код, а затем добавить требуемую реализацию.
* fyi Я тоже переписал класс Студент. Плохое название и странный корпус из-за настойчивости C#, не говоря уже о том, что предоставленный вами код не компилируется. Я предпочитаю _ в качестве лидера частным переменным-членам. Некоторым это не нравится, однако вы новичок в этом, поэтому я оставлю их, потому что их легко заметить.
public class Student
{
private string _name;
private int _id;
private int _mark;
private char _letterGrade;
private Student() // hide default Constructor
{ }
public Student(string name, int id, int mark, char letterGrade) // Constructor
{
if ( string.IsNullOrEmpty(name) )
throw new ArgumentNullException("name");
if ( id <= 0 )
throw new ArgumentOutOfRangeException("id");
_name = name;
_id = id;
_mark = mark;
_letterGrade = letterGrade;
}
// read-only properties - compressed to 1 line for SO answer.
public string Name { get { return _name; } }
public int Id { get { return _id; } }
public int Mark { get { return _mark; } }
public char LetterGrade { get { return _letterGrade; } }
}
"массив связанных списков" для меня не имеет смысла. Это либо связанный список, либо массив (вы описываете связанный список). Я ничего не знаю и о том, и о другом.