[Играет музыка] ДАГ Lloyd: ОК, так что в эта точка в процессе, мы рассмотрели много основам С Мы знаем много о переменных, массивов, указатели, все, что хороший материал. Те, все вроде встроенных посмотреть, как основы, но мы можем сделать больше, верно? Мы можем объединить вещи вместе в интересных способов. И так давайте сделаем это, давайте начнем ветвиться, что С дает нам, и начать создавать свой собственный данные структуры, использующие эти здания блоки вместе, чтобы сделать что-то действительно ценным, полезным. Один из способов сделать это говорить о коллекциях. Так до сих пор мы имели один тип данных Структура для представления коллекции из нравится значения, близкие значения. Это было бы массив. У нас есть коллекции целых чисел, или Коллекции персонажей и так далее. Структуры также отсортировать данные из Структура для сбора информации, но это не для сбора, как ценности. Это, как правило, смесь различных типов данных вместе внутри одного окна. Но это само по себе не используется для цепи вместе или соединить вместе похожи предметы, такие как массив. Массивы идеально подходят для элемент посмотреть, но вспомнить что это очень трудно вставить в массив, если мы вставки в самый конец этого массива. И лучший пример у меня есть потому что это своего рода вставки. Если вспомнить наше видео на сортировку вставками, было много Расходы, связанные с наличием подобрать элементы, и переложить их из пути, чтобы соответствовать что-то в середине вашего массива. Массивы также страдают от другого проблема, которая является негибкость. Когда мы объявляем массив, мы получаем один выстрел на него. Мы получаем сказать, Я хочу это много элементов. Может быть 100, это могло бы быть 1000, это могло бы быть х, где х представляет собой число, пользователь дал нам в приглашении или в команде линия. Но мы получить только один выстрел на него, мы не попасть в то говорить о, на самом деле я необходимо 101 или мне нужно х плюс 20. Слишком поздно, мы уже объявил Массив, и если мы хотим, чтобы получить 101 или х плюс 20, мы должны объявить совершенно другой массив, скопировать все элементы массива над, а затем у нас есть достаточно. А что, если мы не правы, опять же, если мы на самом деле нужно 102 или х плюс 40, мы должны сделать это снова. Так что они очень негибкой для изменения размера наши данные, но если мы объединим вместе некоторые из основ, которые мы уже узнал о указатели и сооружений, в частности, с использованием динамической памяти распределение с таНос, мы может поставить эти части вместе создать новые данные structure-- A односвязанны список мы могли бы say-- что позволяет нам расти и сократится коллекцию значений и мы не будет иметь никакого неиспользуемого пространства. Итак, еще раз, мы называем эту идею, это понятие, связанный список. В частности, в этом видео мы говорить о односвязный список, а потом еще видео мы поговорим о вдвойне связанные списки, которые это просто вариация на тему здесь. Но односвязанны список состоит из узлов, узлы просто быть абстрактной term-- это просто то, что я звоню это своего рода Структура, в основном, я? Просто буду называть его node-- и это узел имеет двух членов, или два поля. Он имеет данные, обычно число, число с плавающей точкой характер, или может быть некоторым другим типом данных, что вы определились с типом DEF. И она содержит указатель на другой узел того же типа. Итак, мы имеем две вещи внутри этот узел, данные и указатель на другой узел. И если вы начинаете визуализировать это, вы можете думать об этом как цепь узлов, соединены друг с другом. У нас есть первый узел, его содержит данные и указатель ко второму узлу, который содержит Данные, и указатель на третьем узле. И вот почему мы называем его Связанный список, они связаны друг с другом. Что делает это специальное Структура узла выглядеть? Ну, если вы помните из нашего видео на определение пользовательских типов, с типа DEF, мы можем определить structure-- и введите определить структуру, как это. tyepdef структуры sllist, а затем я используя значение слова здесь произвольно для обозначения любого типа данных на самом деле. Вы можете перейти на целое число или поплавка, Вы могли бы иметь все, что вы хотите. Это не ограничивается только целые числа, или что-нибудь подобное. Так значение только произвольное Тип данных, а затем указатель на другой узел того же типа. Теперь, есть немного поймать здесь с определения структуры когда это структура самоуправления ссылочной. Я должен иметь временный имя для моего строения. В конце дня я явно хотят называть его SLL узел, что в конечном счете новый назвать часть моего определения типа, но я не могу использовать SLL узел в середине этого. Причина в том, я не создал тип называется SLL узел пока я не ударил этого окончательную точку здесь. До этого момента, я должен иметь еще один способ, чтобы обратиться к этому типу данных. И это само ссылочной тип данных. Это, S тип данных из Структура, которая содержит данные, и указатель на другой Структура одного типа. Поэтому мне нужно, чтобы иметь возможность обратиться к Этот тип данных, по крайней мере временно, так давая ему временное Имя структуры sllist позволяет мне сказать, то я хочу указатель на другой структуры sllist, на структуру sllist звезда, а затем после того как я завершил определение, Теперь я могу назвать этот тип SLL узел. Так вот почему вы видите там временное имя здесь, а постоянный имя здесь. Иногда вы можете увидеть определения структуры, например, что не самостоятельно ссылочной, что не есть имя спецификатор здесь. Было бы просто сказать TYPEDEF-структуру, открыть фигурную скобку, а затем определить его. Но если вы структура само ссылочной, а это один, Вы должны указать временное имя типа. Но в конечном итоге, в настоящее время что мы сделали это, мы можем просто обратиться к эти узлы, агрегаты, эти а SLL узлов для целей в остальной части этого видео. Ладно, так что мы знаем, как создать связанный список узел. Мы знаем, как определить связанный список узлов. Теперь, если мы собираемся, чтобы начать использовать их, чтобы собрать информацию, есть пара операций мы нужно понять и работать. Мы должны знать, как создать связанный список из воздуха. Если нет уже никакого списка, мы хотим, чтобы начать один. Таким образом, мы должны быть в состоянии создать связанный список, мы должны, вероятно, искать по списку ссылок найти элемент мы ищем. Мы должны быть в состоянии вставить новые вещи в списке, мы хотим, чтобы наш список, чтобы иметь возможность расти. И точно так же, мы хотим, чтобы иметь возможность удалить вещи из нашего списка, мы хотим, чтобы наш список, чтобы иметь возможность сокращаться. И в конце нашего программы, особенно если вспомнить, что мы динамического распределения памяти чтобы построить эти списки обычно мы хотим, чтобы освободить все, что память когда мы сделали с ним работать. И поэтому мы должны быть в состоянии удалить Весь связанный список в одном неудачу махом. Так давайте пройдем некоторые из этих операций и как мы могли бы визуализировать их, говорить в псевдокода кода специально. Поэтому мы хотим, чтобы создать связаны список, так что, возможно, мы хочу, чтобы определить функцию с этого прототипа. SLL узел звезда, создавать, и я прохождения в один аргумент, некоторые произвольные данные тип снова, некоторого произвольного типа данных. Но я returning-- эту функцию следует вернуться ко мне указатель, чтобы однократно Связанный список узел. Опять же, мы пытаемся создать связанный список из воздуха, так что мне нужно указатель на что список, когда я сделал. Итак, какие же этапы здесь? Ну, первое, что я собираюсь сделать, это динамически выделить место для нового узла. Опять же, мы создаем его из тонкой воздух, так что мы должны таНос пространства для него. И, конечно, сразу после того как мы таНос, мы всегда проверяем, чтобы убедиться, что наши pointer-- мы не вернемся нуль. Потому что, если мы будем пытаться уважение нулевой указатель, мы собираемся страдают сегментации, и мы не хотим этого. Затем мы хотим, чтобы заполнить в поле, мы хотим, чтобы инициализировать значение поля и инициализировать следующее поле. А потом мы хотим, целью которых в конечном итоге, как Прототип функции indicates-- мы хотим вернуть указатель на SLL узла. Так что сделать это выглядеть визуально? Ну, во-первых, мы собираемся, чтобы динамически выделить место для нового узла SLL, таким образом, мы malloc-- это визуальное представление узла мы только что создали. И мы проверяем, чтобы убедиться, это не null-- в этом случае, картина не будет иметь показано, если это было пустым, мы бы запустить из памяти, так что мы хорошо идти туда. Так что теперь мы к шагу С, инициализировать поле узлы значение. Ну, на основе этой функции звоните, я использую здесь, Похоже, я хочу, чтобы пройти в 6, Так что я 6 в поле значения. Теперь, инициализировать следующее поле. Ну, что я собираюсь сделать там, нет ничего рядом, справа, это единственное, что в списке. Так что в следующий момент в списке? Он не должен указывать на что-либо, правильно. Там нет ничего другого, там, так что концепция мы знаем, что это nothing-- указатели на ничего? Он должен быть, может быть, мы хотим положить пустой указатель там, и я буду представлять нуль указатель, как только красный флажок, мы не можем идти дальше. Как мы увидим немного позже, мы будем иметь в конечном счете цепи стрелок подключения эти узлы вместе, но когда вы нажмете красная коробка, это нуль, мы не можем идти дальше, это конец списка. И, наконец, мы просто хотим, чтобы возвращает указатель на этот узел. Таким образом, мы будем называть его новым, и вернется новый поэтому он может быть использован в все функции его создал. Так что мы идем, Мы создали однократно Связанный список узел из воздуха, и теперь у нас есть список, мы можем работать с. Теперь, скажем, мы уже большой цепи, и мы хотим, чтобы найти что-то в нем. И мы хотим, функцию, которая собирается вернуться истинным или ложным, в зависимости от того, существует ли значение в этом списке. Прототип функции, или Заявление для этой функции, может выглядеть this-- BOOL найти, и то мы хотим, чтобы пройти в двух аргументов. Во-первых, это указатель на Первый элемент связанного списка. Это на самом деле то, что вы будете всегда хотят, чтобы отслеживать, и на самом деле может быть то, что Вы даже поставить в глобальной переменной. После того, как вы создаете список, Вы всегда, всегда хочу, чтобы отслеживать очень Первый элемент списка. Таким образом, вы можете обратиться к все другие элементы, просто по цепочке, без необходимости держать указатели в целости и сохранности каждого элемента. Вам нужно только следить за первым одним, если они все связаны вместе. И тогда вторая вещь мы передаем снова произвольно some-- независимо от типа данных мы ищу там внутри мы надеемся, один из узлов в списке. Итак, какие шаги? Ну, первое, что мы делаем, это мы создаем указатель поперечное указывая на голову списков. Ну, почему мы это делаем, мы уже есть указатель на голову списков, почему бы нам просто не двигаться, что один вокруг? Ну, как я только что сказал,, это действительно важно для нас всегда следить из очень первый элемент в списке. И так на самом деле лучше создать дубликат, что и использовать не для перемещения, поэтому мы никогда не случайно отойти, или мы всегда есть указатель на какой-то момент, который Право на первый элемент списка. Так что лучше, чтобы создать Второй, который мы используем, чтобы двигаться. Тогда мы просто сравнить ли поле значения в этом узле это то, что мы ищем, и, если это нет, мы просто переходим к следующему узлу. И мы продолжаем делать что снова и снова и снова, пока мы не найдем ни элемент, или мы попали null-- мы достигли конца списка, и это не было. Это должно, мы надеемся, звонит колокол для вас, как только линейный поиск, мы просто тиражирование его в однократно связан список структура вместо массива, чтобы сделать это. Так вот пример однократно связанный список. Этот состоит из пять узлов, и у нас есть указатель на голову из список, который называется список. Первое, что мы хотим сделать, это снова, создайте этот указатель обхода. Таким образом, мы теперь имеем два указатели которые указывают на то же самое. Итак, обратите внимание здесь также, я не должны таНос любое пространство для Trav. Я не говорю, Trav равна таНос то, что узел уже существует, что пространство в памяти уже существует. Таким образом, все, что я на самом деле делаю это создавая еще один указатель на него. Я не mallocing дополнительный пространство, просто теперь два указатели указывая на то же самое. Так 2, что я ищу? Ну, нет, так что вместо я будет двигаться к следующему. Так в основном, я бы сказал, Трав равна TRAV рядом. Есть 3, что я ищу, нет. Так что я по-прежнему идти не через, пока в конце концов получить до 6, который является то, что я ищу Для основано на вызов функции У меня в верхней там и так я сделал. Теперь, что если элемент я ищу не в списке, это еще будет работать? Ну, обратите внимание, что список здесь немного отличается, и это еще одна вещь, это Важно со связанными списками, Вы не должны сохранить их в определенном порядке. Вы можете, если хотите, но Вы, возможно, уже заметили, что мы не отслеживать то, что номер элемента мы в. И это своего рода одной сделке, что мы есть со связанным списком стихи массивов, это мы не имеем произвольного доступа больше. Мы не можем просто сказать, я хочу чтобы перейти к 0-й элемент, или 6-й элемент моего массива, который я могу сделать в массиве. Я не могу сказать, что я хочу, чтобы перейти к 0-я элемент или 6-й элемент, или 25-элемент моего связанного списка, нет индекс, связанный с ними. И так действительно не имеет значения если мы сохраним наш список в порядке. Если вы хотите, чтобы вас , конечно, можно, но есть нет причины, почему они должны сохранить в любом порядке. Итак, еще раз, давайте попробуем найти 6 в этом списке. Ну, мы начинаем на начало, мы не находим 6, а затем мы продолжим, не найдя 6, пока мы в конечном итоге не получите здесь. Так что сейчас Trav указывает на узел содержащий 8, а шесть не там. Так что следующий шаг будет чтобы перейти к следующему указателю, так сказать, Trav равна TRAV рядом. Ну, Trav рядом, указано красный коробка есть, является недействительным. Так что некуда идти, и поэтому на данном этапе мы можем сделать вывод, что мы достигли конец связанного списка, и 6 не там. И это будут возвращены ложь в этом случае. ОК, как мы вставляем новый узел в связанном списке? Таким образом, мы смогли создать связанный список из ниоткуда, но мы, вероятно, хотите, чтобы построить цепочку, а не создать кучу различных списков. Мы хотим, чтобы один список, который имеет кучу узлов в нем, а не кучка списков с одного узла. Таким образом, мы не можем просто продолжать использовать Создать функция, которую мы определили ранее, в настоящее время мы вставить в Список, который уже существует. Так данном случае, мы собираемся пройти в двух аргументов, указатель на голову, что связаны список, который мы хотим добавить к. Опять же, это, почему это так Важно, что мы всегда отслеживать, потому это единственный способ мы действительно должны обратиться к весь список просто указатель на первый элемент. Таким образом, мы хотим передать в указатель на первый элемент этого, и все, что мы значение хотите добавить в список. И в конце концов эта функция будет возвращать указатель новому главе связанного списка. Каковы этапы здесь? Ну, как с создания, мы должны динамически выделять места для нового узла, и проверьте, чтобы что мы не хватить памяти, опять же, потому что мы используем таНос. Затем мы хотим, чтобы заполнить и вставьте узел, так поставить номер, все, что Допустимы, в узле. Мы хотим, чтобы вставить узел на начало связанного списка. Там это причина того, что я хочу сделать это, и это может быть, стоит принимать вторую чтобы приостановить видео здесь, и думаю, о том, почему я хотел бы вставить в начале связанный Список. Опять же, я упоминал ранее что он делает на самом деле не имеет значения, если мы сохраним его в любой порядок, так что, возможно, что это ключ. И вы видели, что случилось бы, если бы мы хотел, целью которых или просто на секунду назад, когда мы шли через поиск Вы мог видеть то, что может произойдет, если мы пытались вставить в конец списка. Потому что мы не имеем указатель на конец списка. Таким образом, причина того, что я хотел бы вставить в начале, потому, что я могу сделать это немедленно. У меня есть указатель на начало, и мы увидим это в визуальный в секунду. Но если я хочу, чтобы вставить в конце концов, Я должен начать с самого начала, пройти весь путь к конец, а затем прикрепить его. Так что будет означать, что вставки в конец списка стал бы о п Операция, возвращаясь к обсуждению вычислительная сложность. Это было бы стать о н работы, где также список получил больше, и больше, и больше, это будет становиться все более и труднее лавировать то на конце. Но это всегда очень легко лавировать что-то на в начале, Вы всегда в начале. И мы увидим, визуальный это снова. И то, как только мы сделали, когда мы вставили новый узел, мы хотим, чтобы вернуться к нашей указатель новый глава связанного списка, который так как мы вставки на начало, на самом деле будет указатель на узел мы только что создали. Давайте визуализировать это, потому что я думаю, что это поможет. Так вот наш список, она состоит из четыре элемента, узел, содержащий 15, что указывает на узел содержащий 9, который указывает на узле, содержащем 13, что указывает на узел, содержащий 10, который имеет нулевое Указатель в своем следующем указатель так что это конец списка. Поэтому мы хотим, чтобы вставить Новый узел со значением 12 В начале этого Список, что мы делаем? Ну, во-первых, мы таНос пространство для узел, а затем положить 12 там. Так что теперь мы достигли точка принятия решения, верно? У нас есть несколько указатели, которые мы могли бы двигаться, что надо двигаться, мы в первую очередь? Должны ли мы сделать 12 пункт новый глава list-- или, простите, мы должны сделать 12 указывают на старый главе списка? Или мы должны сказать, что Список в настоящее время начинается в 12 лет. Там это различие там, и мы будем смотреть на то, что происходит с обоими в секунду. Но это приводит к большая тема для боковой панели, который что один из каверзные вещи со связанными списками является организация указатели в правильном порядке. Если вы переместите вещи из того, Вы можете в конечном итоге случайно сиротами остальное списка. И вот тому пример. Итак, давайте с идеей of-- Ну, мы только что создали 12. Мы знаем, 12 будет новый глава списка, и так, почему бы нам просто не перейти указатель Список указать там. ОК, так что это хорошо. Так что теперь, когда делает следующий пункт 12? Я имею в виду, визуально мы видим что будет указывать на 15, как люди это действительно очевидно для нас. Как компьютер знаете? Мы не имеем ничего указывая на 15 больше, верно? Мы потеряли возможность ссылаться на 15. Мы не можем сказать, новый стрелку рядом равных то, нет ничего. На самом деле, мы осиротели остальная часть списка тем самым, мы случайно нарушил цепь. И мы, конечно, не хотите, чтобы сделать это. Итак, давайте вернитесь и попробуйте это снова. Может быть, то, что нужно сделать, это установить следующий указатель 12 в к старому главе списка первого, то мы можем переместить список более. И в самом деле, то есть Правильный порядок, что мы необходимо следовать, когда мы работы с односвязный список. Мы всегда хотим, чтобы подключить Новый элемент в списке, прежде, чем мы принять такую важным шагом изменения где глава связанного списка. Опять же, это такой фундаментальный вещь, мы не хотим, чтобы потерять его. Поэтому мы хотим, чтобы убедиться, что все связаны вместе, прежде чем мы перейдем этот указатель. И так это будет правильный порядок, который является подключение 12 к списку, то говорят, что список начинается 12. Если бы мы сказали список начинается в 12 и затем попытался подключить 12 в список, мы уже видели, что происходит. Мы теряем список по ошибке. Итак, еще одна вещь, чтобы говорить о. Что делать, если мы хотим, чтобы избавиться от весь связанный список сразу? Опять же, мы mallocing Все это пространство, и поэтому мы нужно освободить его, когда мы сделали. Так что теперь мы хотим, чтобы удалить весь связанный список. Ну, то, что мы хотим сделать? Если мы достигли нулевой указатель, мы хотите, чтобы остановить, иначе, просто удалите остальная часть списка, а затем освободить меня. Удалить остальную часть списка, и затем освободить текущий узел. Что это похоже, то, что техника у нас говорили о ранее это звучит как? Удалить все остальные, то вернуться и удалить меня. Это рекурсия, мы сделали Проблема немного меньше, мы говорим удалить всех еще, то вы можете удалить меня. А дальше вниз по дороге, что узел скажу, удалить все остальные. Но в конце концов мы получим в Точка где список пустой, и это наша база случай. Итак, давайте взглянем на это, и как это может работать. Так вот наш список, это то же самое список мы просто говорим, и есть шаги. Там очень много текста здесь, но надеюсь, визуализация поможет. Таким образом, мы have-- и я также вытащил до нашей кадров стека иллюстрации из нашего видео на стеков вызовов, и, надеюсь, все это вместе покажу вам, что происходит. Так вот наш код псевдокод. Если мы достигнем нуль указатель, остановить, иначе, удалить остальную часть списка, Затем освободить текущий узел. Так что сейчас, list-- указатель, что мы передавая уничтожить очков до 12. 12 не является нулевым указателем, поэтому мы собирается удалить остальную часть списка. Что является удаление Остальные из нас участвует? Ну, это означает, делая позвоните, чтобы уничтожить, говоря что 15 это начало Остальная часть списка, который мы хотим уничтожить. И поэтому вызов, чтобы уничтожить 12 вид на удержание. Это заморожены там, ожидая позвоните, чтобы уничтожить 15, чтобы закончить свою работу. Ну, 15 не является нулевым указателем, и так что это, чтобы сказать, все в порядке, хорошо, удалить остальную часть списка. Остальная часть списка начинает в 9, и поэтому мы просто ждать, пока вы не удалите все, что материал, а затем вернуться и удалить меня. Ну 9 скажет, ну, Я не пустой указатель, так что удалите остальные список здесь. И поэтому попробуйте и уничтожить 13. 13 говорит, что я не пустой указатель, То же самое, он передает доллар. 10 не пустой указатель, 10 содержит пустой указатель, но 10 не само по себе является NULL указатель прямо сейчас, и так она проходит доллар тоже. А теперь список точек там, это действительно будет указывать на some-- если у меня было больше места в изображении, это будет указывать на некоторой случайной пространства что мы не знаем, что это такое. Это нулевой указатель, хотя, список буквально теперь установлено, что это значения нулевые. Это указывает прямо в этой красной коробке. Мы достигли нулевой указатель, поэтому мы можем остановить, и мы сделали. И так, что фиолетовый кадра now-- на Верх stack-- это активный кадр, но это делается. Если мы достигли нулевой указатель, остановиться. Мы ничего не делаем, мы не может освободить нулевой указатель, мы не таНос любой пространство, и поэтому мы сделали. Так, что функции кадра разрушается, и мы resume-- мы забрать, где мы оставили от со следующей высшей одного, который это темно-синяя рамка здесь. Таким образом, мы забрать там, где мы остановились. Мы удалили остальную часть Список уже, так что теперь мы собирается освободить текущие узлы. Так что теперь мы можем освободить этот узел, и в настоящее время мы достигли конца функции. И так, что функция кадр будет уничтожен, и мы забрать в голубом один. Так что says-- я уже done-- удаление остальной части списка, так освободить текущий узел. А теперь желтый кадр обратно на вершине стека. И так, как вы видите, мы теперь разрушая список справа налево. Что случилось бы, хотя, Если бы мы сделали то не так? Так же, как когда мы пытались добавить элемент. Если мы перепутались, если цепь мы не подключить указатели в правильном порядке, если мы просто освободил первый элемент, если мы просто освободили голова списка, теперь мы нет никакого способа сослаться на остальная часть списка. И поэтому мы бы сиротами все, мы бы имели то, что называется утечка памяти. Если вы помните из нашего видео на динамическом распределении памяти, это не очень хорошая вещь. Итак, как я уже сказал, несколько операций, что мы должны использовать для работы с связан список эффективно. И вы, возможно, заметили, я опустил одну, удаление одного элемента из связанного Список. Поэтому я сделал это это на самом деле своего рода сложно думать о том, как удалить один элемент из однократно Связанный список. Мы должны быть в состоянии пропустить что-то в списке, который означает, что мы получаем в point-- мы хотите удалить этот node-- но для того, чтобы сделать это так, мы не терять информацию, мы должны связывать это узел здесь, здесь. Так что я, вероятно, сделал это неправильно с визуальной точки зрения. Таким образом, мы находимся в начале нашего Список, мы протекающего через, мы хотим, чтобы удалить этот узел. Если мы просто удалить его, мы нарушили цепочку. Этот узел прямо здесь относится ко всему остальному, он содержит цепь отсюда на. Итак, что мы должны сделать, на самом деле после того как мы добраться до этой точки, что мы должны сделать шаг назад один, и подключить этот узел к этому узлу, так что мы можем затем удалить один в середине. Но поодиночке связанные списки не обеспечить нам путь назад. Таким образом, мы должны либо сохранить два указателя, и переместить их вроде выключения шагом, один за друга, как мы идем, или добраться до точки, а затем отправить другой указатель конца. И, как вы видите, это может получить немного грязно. К счастью, у нас есть еще один способ решить, что когда мы говорим о вдвойне связанные списки. Я Дуг Ллойд, это CS50.