Int main()
{List <int> l;
int value,curIndex,x,y,i;
Ifstream in("input.Txt");
ofstream out("output.txt");
in>>x;in>>y;
//пока не дошли до конца файла, считываем элементы,
while (in>>value)
{
curIndex=l.getLength()+1; //позиция, в которую вставляем узел будет на 1
//больше, чем количество узлов списка
l.insert(curIndex,value);//вставляем узел со значением valueв конец списка
}
In.Close();
for (int i=1;i<=l.getLength();i++)
//счетчик пробегает все позиции в списке
if (l.get(i)==x) //если значение элемента в текущей позиции равно x
{l.insert(i,y); //вставляем новый элемент у в текущую позицию
//при этом элемент со значением х сдвинется на следующую
l.remove(i+1); //удаляем его из списка
}
for (int i=1;i<=l.getLength();i++)
out<<l.get(i)<<" ";
l.~List();
out.close();
return 0;
}
_________________________________input.txt_____________________________
7 0
7 7 1 3 7 5 2 5 7 2 7 9 3 7 7
_____________________________output.txt________________________________
0 0 1 3 0 5 2 5 0 2 0 9 3 0 0
Рассмотрим подробнее, как выполняется данная задача. В цикле сначала вызывается метод get, который по номеру позиции возвращает значение элемента, стоящего на данной позиции в списке. Как выполняется это действие? Внутри метода get вызывается метод find, внутри которого вспомогательный указатель устанавливается на голову списка и начинает двигаться по списку, «отсчитывая» элементы, пока не доберется до нужного. Далее, если найден элемент x, то вызываются по очереди методы insertи remove. Метод insertвставляет данный элемент в данную позицию, для чего опять вызывается метод find, который снова устанавливает указатель на первый элемент и начинает двигаться по списку, пока не дойдет до данного. То же самое, происходит при выполнении метода remove. Конечно, проще один раз найти указатель на данный элемент и работать с ним, но нельзя, чтобы указатель на элемент списка был доступен «извне», это может привести к порче данных (по неосторожности или по безграмотности). Т.о. за удобство и безопасность приходится платить потерей эффективности. Если такую задачу приходится выполнять неоднократно, то в реализацию класса, можно добавить соответствующий метод, который будет иметь доступ к указателям и поэтому не будет выполнять «лишних» действий:
void change(Item x, Item y)
{ elem *cur=head; //устанавливаем вспомогательный указатель curна
//голову списка
while (cur!=NULL) //пока список не пройден
{
if (cur->inf==x) cur->inf=y; //если информационная часть очередного
//элемента равна x, заменяем ее на y
cur=cur->next; //переходим к следующему элементу
}
}
Реализация двухсвязного списка
Мы рассмотрели операции вставки и удаления, которые для двусвязного списка имеют свою специфику. Остальные операции выполняются почти так же, как и в случае односвязного списка, надо только помнить о том, что в каждом узле теперь два указателя.
class listException: public exception
{
public:
listException(const string & message="")
: exception(message.c_str())
{}
};
template <class Item>
class List
{
struct elem
{Item inf;
elem *next;
elem *prev;
elem (Item x):inf(x),next(0),prev(0)
{}
};
elem *head;
elem *tail;
int size;
elem *find(int index)
{
if ((index<1)||(index>size))
return NULL;
else
{
elem *cur=head;
for (int i=1;i<index;i++)
cur=cur->next;
return cur;
}
}
public:
List():head(0),tail(0),size(0){}
~List()
{while (!empty())
remove(1);
}
bool empty()
{return head==0;}
int getLength()
{return size;}
Item get(int index)
{
if ((index<1)||(index>size))
throw listException("StackException: get — list error");
else
{
elem *r=find(index);
Item i=r->inf;
return i;
}
}
void insertLeft(int index, Item data)
//слева от элемента в позиции indexвставляется элемент data
{
int newLength=getLength()+1;
if ((index<1)||(index>size+1))
throw listException("StackException: insert — list error");
else
{
elem *newPtr=new elem(data);
size=newLength;
//ищем указатель на узел в позиции index
elem *cur=find(index);
//если этот указатель пустой, то список был пуст, поэтому новый
//элемент будет и первым и последним
if (cur==NULL)
{
head=newPtr;
tail=newPtr;
}
else
//производим вставку в непустой список, выделяя отдельно случай
//вставки слева от первого элемента
{
newPtr->next=cur;
newPtr->prev=cur->prev;
cur->prev=newPtr;
if (cur==head) head=newPtr;
else
newPtr->prev->next=newPtr;
}
}
}
void insertRight(int index, Item data)
//справа от элемента в позиции indexвставляется элемент data
{
int newLength=getLength()+1;
if ((index<1&&head!=NULL)||(index>size+1))
throw listException("StackException: insert — list error");
else
{
elem *newPtr=new elem(data);
size=newLength;
//ищем указатель на узел в позиции index
elem *cur=find(index);
//если этот указатель пустой, то список был пуст, поэтому новый
//элемент будет и первым и последним
if (cur==NULL)
{
head=newPtr;
tail=newPtr;
}
else
//производим вставку в непустой список, выделяя отдельно случай
//вставки справа от последнего элемента
{
newPtr->next=cur->next;
newPtr->prev=cur;
cur->next=newPtr;
if (cur==tail)
tail=newPtr;
else
newPtr->next->prev=newPtr;
}
}
}
void remove(int index)
//удалениеэлементавпозиции index
//выделяются частные случаи:
//когда список после удаления пуст (тогда нужно присвоить head=NULL;tail=NULL;)
//когда удаляемый элемент первый
//когда удаляемый элемент последний
//общийслучай
{
if ((index<1)||(index>size))
throw listException("StackException: remove — list error");
else
{elem *cur=find(index);
--size;
if (size==0)
{head=NULL;tail=NULL;}
else
if (cur==head)
{ head=head->next;
head->prev=NULL;
}
else
if (cur==tail)
{
tail=tail->prev;
tail->next=NULL;
}
else
{cur->prev->next=cur->next;
cur->next->prev=cur->prev;
}
cur->next=NULL;
cur->prev=NULL;
delete cur;
}
}
};
2.6. Решение практических задач с использованием
двунаправленного списка
1. В файле input.txtзаписана последовательность целых чисел. Создать на основе нее список, заменить в нем каждый элемент xна у. Полученный список переписать в файл output.txt
#include "exception"
#include "iostream"
#include "fstream"
#include "string"
using namespace std;
//описывается соответствующий класс
intmain()
{List <int> l;
int value,curIndex,x,y,i;