Дан список из n целых чисел a1, a2,...,an.

Модераторы: Hawk, Romeo, Absurd, DeeJayC, WinMain

Ответить
FunkyDanky
Сообщения: 4
Зарегистрирован: 24 дек 2015, 16:18
Откуда: Россия

Всем доброго времени суток, у меня тут такая ситуация, что не могу найти где у меня ошибка, помогите пожалуйста кто с чем сможет. void printlist(list* head, list* tail) эта процедура не работает, по крайней мере компилятору это не нравится. Заранее, очень благодарен.
Дан список из n целых чисел a1, a2,...,an.
1) Вывести на экран элементы списка в указанной последовательности: an, an-1,...,a1.
2) Удалить из списка все элементы, входящие в него в точности 2 раза.
Желательно вывести список до и списки после, это тут получается 2 задачи в одной.
В решениях задач этого раздела необходимо использовать динамические структуры данных - линейные одно- или двусвязные списки(последовательности значений). Следует заметить, что реорганизация списка(включение, исключение, перестановка элементов) должна осуществляться изменением ссылочных, а не информационных полей элементов списка. Кроме того считается, что перед выполнением любой операции со списком его длина(количество элементов) явно не задана.
(Версия Visual Studio 2013)

Код: Выделить всё

#include <iostream>
#include <iomanip>
#include <stdlib.h>
#include <locale.h>
#include <map>


using namespace std;

struct list
{
	int info;
	list *pred, *next;
};

/*Определяем прототипы будущих функций*/
void insert(list * head, list * tail, list * p);
void makelist(list * & head, list * & tail);
void printlist(list* head, list* tail);
void Invertlist(list* head, list* tail);
void Del_Sam(list * & head, list * & tail);

/**/
void main()
{
	setlocale(LC_ALL, "Russian");
	list *head, *tail;

	makelist(head, tail);
	cout << "Исходный список:" << endl;

	printlist(head, tail);

	cout << endl << "Перевернутый список:" << endl;
	Invertlist(head, tail);

	Del_Sam(head, tail);
	cout << endl << "Удалены элементы, входящие в точностии 2 раза:" << endl;
	printlist(head, tail);


	cout << "" << endl;
	system("pause");
}

/**/
void insert(list * head, list * tail, list * p)
{
	if (p)
	{
		p->next = tail;
		p->pred = tail->pred;
		tail->pred = p;
		p->pred->next = p;
	}
	else
		cout << "Ошибка!" << endl;
}

/*Процедура создания списка*/
void makelist(list * &head, list * &tail)
{
	head = new list;
	tail = new list;
	head->next = tail;
	tail->pred = head;
	cout << "Введите числа до 0:" << endl;
	int k;
	cin >> k;
	while (k != 0){
		list*p = new list;
		p->info = k;
		insert(head, tail, p);
		cin >> k;
	}
	return;
}

/*Процедура вывода списка на экран*/
void printlist(list* head, list* tail)
{
	if (head && tail)
	{
		list * p = head->next;
		while (p != tail){
			cout << p->info << ends;
			p = p->next;
		}
	}
	return;
}

/*Процедура удаления списков элемента, входящие в него в точности 2 раза*/
void Del_Sam(list * & head, list * & tail)
{
	list * p = head; // Указываем на голову
	map <int, int> myMap; // Используем map для хранение пары значение -> количество
	while (p)
	{
		myMap[p->info]++; // Считаем количество одинаковых элементов
		p = p->next;
	}

	p = head; // p присваиваем к голове

	while (p)
	{
		if (myMap[p->info] == 2) // Если элементов было два
		{
			(p->next)->pred = p->pred; // Переставляем указатель следующего элемента
			(p->pred)->next = p->next; // Переставляем указатель предыдущего элемента
			list * t = p;
			p = p->next;
			delete p; 
		}
		else
			p = p->next;
	}
}

/*Процедура перевертывания списка*/
void Invertlist(list* head, list* tail)
{
	if (head && tail)
	{
		list * p = tail->pred; // Идем с конца списка
		while (p != head) // Пока p не дошел до головы
		{
			cout << p->info << ends;
			p = p->pred;
		}
	}
	return;
}
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

Какова нагрузка слова "дан" в названии темы. Ну дан список. Наконецто. Зачем это надо знать мне? Что дан именно список, а не массив, сказано дальше в том же названии и вот эта информация там нужна. А что он дан не нужно. Хорошо, что название получилось коротким. А если бы заметно длиннее? Тогда попытка сформулировать его как предложение добавляет длину на столько, что оно может не влезть в ограничение. Но это офтоп. А теперь по теме:
1. В функции insert не используется head. Зачем он передаётся?
2. Указатель head указывает перед началом списка, а tail - за конец, при этом по этим указателям фактически располагаются ещё два элемента списка, но они не используются.
3. Признак пустого списка - равенство tail-pred и head, это во-первых проверка двух указателей на равенство, а во-вторых ещё и косвенная адресация. Такая проверка занимает больше времени. Лучше сделать признаком не равенство tail или head и nullptr.
4. Цикл в функции Del_Sam опасен выходом за границы: нет гарантии ложности tail->next, но p гарантированно получит это значение после tail.
5. Хотя head указывает перед началом, но цикла в Del_Sam начинается с него.
6. Хотя tail указывает за конец, но цикл в Del_Sam дойдёт до него.

Код: Выделить всё

#include <iostream>
#include <iomanip>
#include <stdlib.h>
#include <locale.h>
#include <map>
#include <new>


using namespace std;

struct list
{
    int info;
    list *pred, *next;
};

/*Определяем прототипы будущих функций*/
void insert(list * head, list * tail, list * p);
void makelist(list * & head, list * & tail);
void printlist(list* head, list* tail);
void Invertlist(list* head, list* tail);
void Del_Sam(list * & head, list * & tail);

/**/
void main()
{
    setlocale(LC_ALL, "Russian");
    list *head=nullptr, *tail=nullptr;

    makelist(head, tail);
    cout << "Исходный список:" << endl;

    printlist(head, tail);

    cout << endl << "Перевернутый список:" << endl;
    Invertlist(head, tail);

    Del_Sam(head, tail);
    cout << endl << "Удалены элементы, входящие в точностии 2 раза:" << endl;
    printlist(head, tail);


    cout << "" << endl;
    system("pause");
}
cout << "Ошибка!" << endl;
/**/
void insert(list * head, list * tail, list * p)
{
    if (p!=nullptr)
    {
     if (tail==nullptr)
     {
      tail=p;
      tail->next=nullptr;
      tail->pred=nullptr;
      head=tail;
     }
     else
     {
        p->next = tail;
        p->pred = tail->pred;
        tail->pred = p;
        p->pred->next = p;
        if (tail==head)
        {
         head=p;
        }
    }
   }
   else
        cout << "Ошибка!" << endl;
}

/*Процедура создания списка*/
void makelist(list * &head, list * &tail)
{
    cout << "Введите числа до 0:" << endl;
    int k;
    cin >> k;
    while (k != 0){
        list*p = new (std::nothrow) list;
        p->info = k;
        insert(head, tail, p);
        cin >> k;
    }
    return;
}

/*Процедура вывода списка на экран*/
void printlist(list* head, list* tail)
{
    if (head!=nullptr)
    {
        list * p = head;
        while (p != nullptr){
            cout << p->info << ends;
            p = p->next;
        }
    }
    return;
}

/*Процедура удаления списков элемента, входящие в него в точности 2 раза*/
void Del_Sam(list * & head, list * & tail)
{
    list * p = head; // Указываем на голову
    map <int, int> myMap; // Используем map для хранение пары значение -> количество
    while (p)
    {
        myMap[p->info]=0; // Считаем количество одинаковых элементов
        p = p->next;
    }
    p=head;
    while (p)
    {
        myMap[p->info]++; // Считаем количество одинаковых элементов
        p = p->next;
    }

    p = head; // p присваиваем к голове

    while (p)
    {
        if (myMap[p->info] == 2) // Если элементов было два
        {
            (p->next)->pred = p->pred; // Переставляем указатель следующего элемента
            (p->pred)->next = p->next; // Переставляем указатель предыдущего элемента
            list * t = p;
            p = p->next;
            delete p;
        }
        else
            p = p->next;
    }
}

/*Процедура перевертывания списка*/
void Invertlist(list* head, list* tail)
{
    if (head!=nullptr)
    {
        list * p = tail; // Идем с конца списка
        while (p != nullptr) // Пока p не дошел до головы
        {
            cout << p->info << ends;
            p = p->pred;
        }
    }
    return;
}
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
FunkyDanky
Сообщения: 4
Зарегистрирован: 24 дек 2015, 16:18
Откуда: Россия

Если вы имеете что-то против названия, то я просто скопировал первой предложение текста, не более... Насчет компиляции кода вашей программы, она не компилируется, но если я убираю с 45 строку( cout << "Ошибка!" << endl ; ) то уже все нормально, казалось бы, но на консольном приложении мне выдает вовсе не то) :confused: Изображение
FunkyDanky
Сообщения: 4
Зарегистрирован: 24 дек 2015, 16:18
Откуда: Россия

:confused:
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Не знаю, куда пропал Сионист, так что я решил сам глянуть код. Сразу скажу, что код Сиониста не смотрел, посмотрел только исходный. Проблемы в нём следующие:

1) Непонятно, зачем сделаны фиктивные head и tail. Обычно так не принято делать.

2) Указатели head-prev и tail->next не инициализированы (содержат мусор). Это не проблема, пока все циклы у нас бегут до head или tail, не включая оных, но станет проблемой, если цикл начнётся или окончится, включая эти указатели.

3) Функция Invertlist не разворачивает список физически, а лишь печатает его задом наперёд. Хотя, я так думаю, задание подразумевает именно физическое инвертирование.

4) Функция Del_Sam написана явно другим человеком, и в ней, как раз, самая главная проблема. Дело в том, что она использует классическое представление списка, а не а то, которые ты выбрал изначально (с фиктивными head и tail). Все циклы тут бегут начиная с head и заканчивая NULL, а так как tail->next у нас не инициализирован, то циклы получаются бесконечными.

Из-за четвёртой проблемы как раз всё не работает. Следует либо переписать функцию на фиктивные head и tail, либо переписать остальной код на классический список.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
FunkyDanky
Сообщения: 4
Зарегистрирован: 24 дек 2015, 16:18
Откуда: Россия

Romeo писал(а): Из-за четвёртой проблемы как раз всё не работает. Следует либо переписать функцию на фиктивные head и tail, либо переписать остальной код на классический список.

Большое спасибо, вроде разобрался, тот код реально другой человек писал...
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

4) Функция Del_Sam написана явно другим человеком, и в ней, как раз, самая главная проблема. Дело в том, что она использует классическое представление списка, а не а то, которые ты выбрал изначально (с фиктивными head и tail). Все циклы тут бегут начиная с head и заканчивая NULL, а так как tail->next у нас не инициализирован, то циклы получаются бесконечными.

Из-за четвёртой проблемы как раз всё не работает.
Вот её то я и решал, переделав указатели на концы списка в привычные мне. Но то, что здесь другой автор, мне в голову не пришло. Я думал, что автор тот же, просто сам же запутался в том, куда у него указывают head и tail.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Ответить