Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.80/15: Рейтинг темы: голосов - 15, средняя оценка - 4.80
4 / 1 / 0
Регистрация: 09.10.2015
Сообщений: 204
1

Сортировка слиянием кольцевого списка

23.10.2016, 23:37. Показов 2767. Ответов 20
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Есть класс двусвязного кольцевого списка и итератор к нему-шаблоны.
не могу довести до ума сортировку слиянием для этого списка и понять в чем проблема((
//tail->next=первый
//tail->prev=последний
//tail-узел ограничитель.в поле val-лежит мусор
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
#include <iostream>
using namespace std; 
 
template <class T>
class Double_list
{
public:
    struct Double_node
    {
        Double_node *next;
        Double_node *prev;
        int val;
        Double_node() {}
        Double_node(T x)
        {
            val = x;
        }
        Double_node(T a, Double_node* b, Double_node* c)
        {
            val = a;
            next = b;
            prev = c;
        }
    };
    class iterator
    {
        public:
        Double_node* node;
    public:
        iterator(Double_node* tmp)
        {
            node = tmp;
        }
        T& operator*() const
        {
            return node->val;
        }
        iterator operator++(int)
        {
            iterator it(node);
            node = node->next;
            return it;
        }
        iterator operator--(int)
        {
            iterator it(node);
            node = node->prev;
            return it;
        }
        iterator operator++()
        {
            node = node->next;
            return *this;
        }
        iterator operator--()
        {
            node = node->prev;
            return *this;
        }
        bool operator==(iterator it)
        {
            return it.node == this->node;
        }
        bool operator!=(iterator it)
        {
            return it.node != this->node;
        }
        iterator operator+(int n)
        {
            iterator p = node;
            for (int i = 0; i < n; i++)
            {
                p++;
            }
            return p;
        }
        iterator operator-(int n)
        {
            iterator p = node;
            for (int i = 0; i < n; i++)
            {
                p--;
            }
            return p;
        }
        int operator-(iterator  q)
        {
            int counter = 0;
            iterator p = node;
            for (q; q != p; q++)
            {
                counter++;
            }
            return counter;
        }
    };
    void merge_sort_rec(iterator first, iterator last)
    {
        int we_need = last - first;
        if (we_need> 1)
        {
            iterator mid = first + sz / 2;
            merge_sort_rec(first, mid);
            merge_sort_rec(mid, last);
            merge(first, mid, last);
        }
    }
    void merge(iterator first, iterator middle, iterator last)
    {
        iterator f1 = first;
        iterator f2 = middle;
        Double_node* tmp;
        while (f2 != last && f1 != middle)//посл из первой половины указ на мид
        {
            if (*f1 < *f2)
            {
                tmp = f1.node->next;
                f2.node->prev->next = f1.node;
                f1.node->prev = f2.node->prev;
                f1.node->next = f2.node;
                f2.node->prev = f1.node;
                f1.node = tmp;
            }
            else
            {
                tmp = f2.node->next;
                f1.node->prev->next = f2.node;
                f2.node->prev = f1.node->prev;
                f2.node->next = f1.node;
                f1.node->prev = f2.node;
                f2.node = tmp;
            }
        }
    }
    private:
        Double_node *tail;
        int sz;
    public:
        //tail->next=первый
        //tail->prev=последний
        //tail-узел ограничитель.в поле val-лежит мусор
        iterator rbegin()
        {
            return iterator(tail->next);
        }
        iterator rend()
        {
            return iterator(tail);
        }
        Double_list();
        ~Double_list();
        bool is_empty();
        void add_front(T);
        void add_rear(T);
        void insert_after(int, T);
        T remove(int);
        T remove_front();
        T remove_rear();
        bool find(T);
        T get_nth(int);
        int size();
        void sort()
        {
            iterator p = rbegin();
            iterator q = rend();
            merge_sort_rec(p, q);
        }
        void insert(iterator it, T x)
        {
            Double_node* mem_next = it.node->next;
            Double_node* added = new Double_node(x);
            it.node->next = added;
            added->prev = it.node;
            added->next = mem_next;
            mem_next->prev = added;
            sz++;
        }
        void remove_iter(iterator it)
        {
            Double_node* mem = it.node;
            Double_node* mem_next = it.node->next;
            Double_node* mem_prev = it.node->prev;
            mem_prev->next = mem_next;
            mem_next->prev = mem_prev;
            delete it.node;
            sz--;
        }
        void print()
        {
            Double_node* i = tail->next;
            while (i != tail)
            {
                cout << i->val << ' ';
                i = i->next;
            }
            cout << endl;
        }
    };
 
 
template <class T> Double_list<T>::Double_list()
{
    tail = new Double_node;
    tail->prev = tail;
    tail->next = tail;
    sz = 0;
}
 
template<class T>  Double_list<T>::~Double_list()
{
     while (!tail) 
     {
     Double_node* tmp=tail->next;
     delete tail;
     tail = tmp;
     } 
     delete tail;
     sz = 0; 
}
 
template<class T>  int Double_list<T>::size()
{
    return sz;
}
 
template<class T>  bool Double_list<T>::is_empty()
{
    return sz == 0;
}
 
template<class T>  void Double_list<T>::add_front(T x)
{
    if (sz == 0)
    {
        Double_node* add = new Double_node(x);
        add->prev = tail;
        tail->next = add;
        add->next = tail;
        tail->prev = add;
    }
    else
    {
        Double_node* add = new Double_node(x);
        Double_node* add_next = tail->next;
        tail->next = add;
        add->prev = tail;
        add_next->prev = add;
        add->next = add_next;
    }
    sz++;
}
 
template<class T>  void Double_list<T>::add_rear(T x)
{
    if (sz == 0)
    {
        add_front(x);
    }
    else
    {
        Double_node* tail_prev = tail->prev;
        Double_node* add = new Double_node(x);
        tail->prev = add;
        add->next = tail;
        tail_prev->next = add;
        add->prev = tail_prev;
        sz++;
    }
    
}
template<class T>  void Double_list<T>::insert_after(int n, T x)
{
    
    if (sz > n)
    {
        Double_node* mem = tail;
        for (int i = 0; i < n; i++)
        {
            mem = mem->next;
        }
        Double_node* mem_next = mem->next;
        Double_node* add = new Double_node(x);
        add->next = mem_next;
        mem_next->prev = add;
        mem->next = add;
        add->prev = mem;
        sz++;
    }
}
 
template<class T>  T Double_list<T>::remove(int n)
{
    Double_node* mem = tail;
    for (int i = 0; i < n; i++)
    {
        mem = mem->next;
    }
    T memory = mem->val;
    mem->next->prev = mem->prev;
    mem->prev->next = mem->next;
    delete mem;
    sz--;
    return memory;
}
 
template<class T>  T Double_list<T>::remove_front()
{
    
    T memory = tail->next->val;
    Double_node* removeable = tail->next;
    tail->next->next -> prev = tail;
    tail->next =removeable->next;
    delete removeable;
    sz--;
    return memory;
}
 
template<class T>  T Double_list<T>::remove_rear()
{
    
    T memory = tail->prev->val;
    Double_node* removeable = tail->prev;
    tail->prev = tail->prev->prev;
    tail->prev->prev->next = tail;
    delete removeable;
    sz--;
    return memory;
}
 
template<class T>  bool Double_list<T>::find(T x)
{
    Double_node* mem = tail=>next;
    while (mem != tail)
    {
        if (mem->val == x)
        {
            return true;
        }
        mem = mem->next;
    }
    return false;
}
 
template<class T>  T Double_list<T>::get_nth(int n)
{
    Double_node* mem = tail->next;
    for (int i = 0; i < n; i++, mem = mem->next);
    return mem->val;
}
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
23.10.2016, 23:37
Ответы с готовыми решениями:

Сортировка кольцевого двусвязного списка (пузырьковая)
Доброго дня! Помогите пожалуйста разобраться с сортировкой кольцевого двухсвязного списка. У меня...

Сортировка линейного списка слиянием сверху вниз
«Функция merge должна сливать список, на который указывает a, со списком, на который указывает b, с...

Шаблон однонаправленного кольцевого списка
Доброго времени суток. Мне необходимо написать шаблон однонаправленного кольцевого списка. ...

Создание кольцевого однонаправленного списка
Можно ли исправить функцию, чтобы создание происходило сразу всего списка как массива, не вводя...

20
1394 / 1023 / 325
Регистрация: 28.07.2012
Сообщений: 2,813
24.10.2016, 00:00 2
Kristina_S, раз взялась делать итераторы, то ими и пользуйся.
Реализуй сортировку слиянием, которая работает для любой пары итераторов (будь то итераторы из std::vector или другие). Затем поправь свой итератор так, что бы он работал с твоей сортировкой.
0
4 / 1 / 0
Регистрация: 09.10.2015
Сообщений: 204
24.10.2016, 00:05  [ТС] 3
с итераторами все хорошо.да и алгоритм сортировки я понимаю прекрасно и код написала внятный.просто там ошибка времени выполнения и я не знаю как ее убрать
0
1394 / 1023 / 325
Регистрация: 28.07.2012
Сообщений: 2,813
24.10.2016, 00:32 4
Цитата Сообщение от Kristina_S Посмотреть сообщение
код написала внятный
Ну не надо. В твоей сортировке с жонглированием указателями в итераторах черт ногу сломит.
Предлагаю первым делом разобраться, почему вот в этом примере
C++
1
2
3
4
5
6
    Double_list<int> l;
    l.add_front(1);
    l.add_front(2);
    l.print();
    l.merge_sort_rec(l.rbegin(), l.rend());
    l.print();
merge_sort_rec все ломает.
0
4 / 1 / 0
Регистрация: 09.10.2015
Сообщений: 204
24.10.2016, 00:45  [ТС] 5
хм,я так понимаю у меня середина как-то неправильно находится? или не в этом дело?
0
1394 / 1023 / 325
Регистрация: 28.07.2012
Сообщений: 2,813
24.10.2016, 00:57 6
Kristina_S, я думаю, что дело в твоих адовых указателях.
Меняешь например начальный узел с конечным во время сортировки и все.
Начало в конце, конец в начале, где новый конец, а где начало, куда дальше двигаться - непонятно.

Ты забыла одну небольшую вещь - ты должна сортировать значения внутри узлов, а не сами узлы.
Попробуй переписать свою сортировку так, что бы ни разу не обращаться к iterator::node (обращаясь только к публичным методам).
0
4 / 1 / 0
Регистрация: 09.10.2015
Сообщений: 204
24.10.2016, 01:31  [ТС] 7
я не понимаю логики,мне казалось что без буфера сортировать можно если перенаправлять указатели а не переписывать сами значения в узлах. можешь хотя бы на псевдокоде показать что и как?

Добавлено через 2 минуты
проблема кроется в merge или merse_sort_rec?
0
Вездепух
Эксперт CЭксперт С++
11696 / 6375 / 1724
Регистрация: 18.10.2014
Сообщений: 16,078
24.10.2016, 01:38 8
Цитата Сообщение от nonedark2008 Посмотреть сообщение
Ты забыла одну небольшую вещь - ты должна сортировать значения внутри узлов, а не сами узлы.
Наоборот, скорее всего цель всего этого - именно в сортировке самих узлов путем исправления ссылок. Т.е. это аналог std::list<>::sort.
0
4 / 1 / 0
Регистрация: 09.10.2015
Сообщений: 204
24.10.2016, 01:39  [ТС] 9
C++
1
2
3
4
5
6
7
Double_list<int> l;
    l.add_front(1);
    l.add_front(2);
    l.add_front(3);
    l.print();
    l.merge_sort_rec(l.rbegin(), l.rend());
    l.print();
Вот.а сам код проги в шапке темы.
0
1394 / 1023 / 325
Регистрация: 28.07.2012
Сообщений: 2,813
24.10.2016, 01:49 10
Kristina_S,
Цитата Сообщение от Kristina_S Посмотреть сообщение
без буфера сортировать можно если перенаправлять указатели
Можно. К тому же сортировку слиянием и так можно реализовать без использования дополнительного буфера, об этом можно почитать в третьем томе Кнута.

Цитата Сообщение от Kristina_S Посмотреть сообщение
проблема кроется в merge или merse_sort_rec?
В merge я так думаю.

Цитата Сообщение от Kristina_S Посмотреть сообщение
можешь хотя бы на псевдокоде показать что и как?
Не могу, ибо не знаю как. Я не хочу придумывать на ходу алгоритм сортировки циклического списка...
Можешь попробовать перед сортировкой разорвать цикл в списке, отсортировать, а потом опять зациклить. Будет чуть проще.
0
4 / 1 / 0
Регистрация: 09.10.2015
Сообщений: 204
24.10.2016, 01:52  [ТС] 11
ясно.спасибо за дельные советы
0
1394 / 1023 / 325
Регистрация: 28.07.2012
Сообщений: 2,813
24.10.2016, 02:11 12
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
скорее всего цель всего этого - именно в сортировке самих узлов путем исправления ссылок. Т.е. это аналог std::list<>::sort
Может быть и так. Кстати, заглянул в исходники, в std::list как раз под капотом такой merge sort и сидит.
0
Вездепух
Эксперт CЭксперт С++
11696 / 6375 / 1724
Регистрация: 18.10.2014
Сообщений: 16,078
24.10.2016, 02:23 13
Цитата Сообщение от Kristina_S Посмотреть сообщение
проблема кроется в merge
На данном тестовом примере проблема, разумеется, в merge, ибо в данном примере никто, кроме merge, никакой фактической работы не делает.

Однако стоит заметить, что данная реализация сортировки практической ценности не представляет. Вся идея сортировки списка заключается в том, что она не требует прямого (индескного) доступа и аналогичных операций. Поэтому ни о каком int we_need = last - first; и iterator mid = first + sz / 2; в грамотно реализованной сортировке списка речи быть не может. То есть никаких пробеганий по списку с целью подсчета элементов или с целью поиска i-того элемента быть не должно. Даже у итератора не должно быть операций + и -. Такие операции есть только у random access iterator. У вас же - bidirectional iterator, который сам по себе в принципе не должен поддерживать + и -.

Для реализации merge sort на списке не надо прыгать на средний элемент. Это в массивах так делают. В списке можно прекрасно обойтись без этого.

Цитата Сообщение от nonedark2008 Посмотреть сообщение
в std::list как раз под капотом такой merge sort и сидит.
Именно так. Причем там сидит именно грамотно реализованный merge sort, который ни в коем случае не будет пытаться эмулировать random access iterators на основе итераторов списка.
0
1394 / 1023 / 325
Регистрация: 28.07.2012
Сообщений: 2,813
24.10.2016, 02:25 14
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Поэтому ни о каком int we_need = last - first; и iterator mid = first + sz / 2; в настоящей сортировке списка речи быть не может.
Хоть внутри итератора этого быть и не должно, но середину то мы как-то отыскать должны?
Значит нам нужен и размер списка, и возможность от начала добраться до середины.

Добавлено через 1 минуту
Смысл в сортировке слиянием, если она "не умеет" разбивать контейнер на две половинки?
0
Вездепух
Эксперт CЭксперт С++
11696 / 6375 / 1724
Регистрация: 18.10.2014
Сообщений: 16,078
24.10.2016, 02:40 15
Цитата Сообщение от nonedark2008 Посмотреть сообщение
Хоть внутри итератора этого быть и не должно, но середину то мы как-то отыскать должны?
Значит нам нужен и размер списка, и возможность от начала добраться до середины.
Реализовывать алгоритм merge sort на списке по принципу "сверху-вниз", т.е. периодическим итеративным беганьем по списку для поиска середины - это детский сад. Список не поддерживает прямого доступа, по каковой причине стратегию "сверху-вниз" к нему осмысленно применить не получится.

Списки сортируют merge sort по стратегии "снизу-вверх" (https://en.wikipedia.org/wiki/... sing_lists), где совершенно не требуется заниматься подобной ерундой.

Добавлено через 2 минуты
Например, реализация в GCC (https://gcc.gnu.org/onlinedocs... tml#l00355) работает именно снизу-вверх, как и реализация в MSVC.

Добавлено через 7 минут
Цитата Сообщение от nonedark2008 Посмотреть сообщение
Смысл в сортировке слиянием, если она "не умеет" разбивать контейнер на две половинки?
Сортировка слиянием на списке разбивает список на половинки, но по совсем другому принципу:

1. Мы считаем, что одна половинка - это четные элементы, а другая - нечетные. Идя слева-направо, делаем попарное слияние одноэлементных списков в списки длины 2.
2. Теперь считаем, что одна половинка - это четные списки длины 2, а другая - нечетные списки длины 2. Идя слева-направо, делаем попарное слияние таких списков в списки длины 4.
2. Теперь считаем, что одна половинка - это четные списки длины 4, а другая - нечетные списки длины 4. Идя слева-направо, делаем попарное слияние таких списков в списки длины 8.

И т.д.

И всю эту обработку можно аккуратно выполнить за один проход исходного списка слева-направо (разумеется, не за O(n), ибо при слиянии подсписков их придется перепроходить снова).

Другими словами, половинки есть и в этом случае. Но они получаются не путем разрезания списка посередине, а путем рассмотрения списка как двух гребенок, вложенных одна в другую зубьями.
0
1394 / 1023 / 325
Регистрация: 28.07.2012
Сообщений: 2,813
24.10.2016, 02:43 16
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Списки сортируют merge sort по стратегии "снизу-вверх"
Понятно, спасибо.

Цитата Сообщение от TheCalligrapher Посмотреть сообщение
как и реализация в MSVC
Тут я не соглашусь, вот свежевыдернутые исходники из MSVC:
Кликните здесь для просмотра всего текста

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
template<class _Pr2>
        iterator _Sort(iterator _First, iterator _Last, _Pr2& _Pred,
            size_type _Size)
        {   // order [_First, _Last), using _Pred, return new first
            // _Size must be distance from _First to _Last
        if (_Size < 2)
            return (_First);    // nothing to do
 
        iterator _Mid = _STD next(_First, _Size / 2);
        _First = _Sort(_First, _Mid, _Pred, _Size / 2);
        _Mid = _Sort(_Mid, _Last, _Pred, _Size - _Size / 2);
        iterator _Newfirst = _First;
 
        for (bool _Initial_loop = true; ; _Initial_loop = false)
            {   // [_First, _Mid) and [_Mid, _Last) are sorted and non-empty
            if (_DEBUG_LT_PRED(_Pred, *_Mid, *_First))
                {   // consume _Mid
                if (_Initial_loop)
                    _Newfirst = _Mid;   // update return value
                splice(_First, *this, _Mid++);
                if (_Mid == _Last)
                    return (_Newfirst); // exhausted [_Mid, _Last); done
                }
            else
                {   // consume _First
                ++_First;
                if (_First == _Mid)
                    return (_Newfirst); // exhausted [_First, _Mid); done
                }
            }
        }


Волшебная строчка оттуда:
C++
1
iterator _Mid = _STD next(_First, _Size / 2);
Думаю, то, что делает next с двунаправленным итератором, в комментариях не нуждается.
1
Вездепух
Эксперт CЭксперт С++
11696 / 6375 / 1724
Регистрация: 18.10.2014
Сообщений: 16,078
24.10.2016, 02:53 17
Цитата Сообщение от nonedark2008 Посмотреть сообщение
Думаю, то, что делает next с двунаправленным итератором, в комментариях не нуждается.
Это интересная тема. Я хорошо помню, что во времена Dinkumware-вской реализации стандартной библиотеки MSVC использовал тот же алгоритм "onion chaining", что виден по ссылке для GCC.

Вполне может быть, что, к моему удивлению, эти реализации не отличаются по производительности. При этом в лоб используется рекурсия! Странно...

(Ну, по крайней мере, они хоть не перевычисляют длину списка заново на каждом уровне слияния.)
0
1394 / 1023 / 325
Регистрация: 28.07.2012
Сообщений: 2,813
24.10.2016, 03:00 18
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Это интересная тема. Я хорошо помню, что во времена Dinkumware-вской реализации стандартной библиотеки MSVC использовал тот же алгоритм "onion chaining", что виден по ссылке для GCC.
Быстренько проверил у себя:
VS2010/13 - реализация схожа с GCC.
VS2015 - именно его я кидал выше.

Не по теме:

Цитата Сообщение от TheCalligrapher Посмотреть сообщение
эти реализации не отличаются по производительности
Если будет время, то потом проверю.
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
по крайней мере, они хоть не перевычисляют длину списка заново на каждом уровне слияния
Они просто хранят размер списка внутри контейнера - халявщики.

0
Вездепух
Эксперт CЭксперт С++
11696 / 6375 / 1724
Регистрация: 18.10.2014
Сообщений: 16,078
24.10.2016, 03:03 19
Цитата Сообщение от nonedark2008 Посмотреть сообщение
Они просто хранят размер списка внутри контейнера - халявщики.
Это фактически требование C++11, который теперь настаивает на том, чтобы std::list<>::size() выполнялся за O(1).

GCC, кстати, отказался соблюдать это требование стандарта и не хранит размер (т.е. его std::list<>::size() выполняется за O(n), вопреки требованиям стандарта).

Понятно, что наличие хранимого размера делает данный алгоритм более привлекательным, но меня все равно меня смущает iterator _Mid = _STD next(_First, _Size / 2);. Неужели оно стоит того?
0
4 / 1 / 0
Регистрация: 09.10.2015
Сообщений: 204
24.10.2016, 12:05  [ТС] 20
так мне кто нибудь поможет кодом ,ребята?
0
24.10.2016, 12:05
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
24.10.2016, 12:05
Помогаю со студенческими работами здесь

Реализация кольцевого списка на СТЛ
Добрый день! Появилось несколько вопросов в ходе выполнения задания. Само задание звучит так: ...

Нахождение и изменение элемента двунаправленного кольцевого списка
помогите дополнить код,что бы можно было найти элемент введенный с клавиатуры и заменить его другим...

Консольный интерфейс для кольцевого односвязного списка
http://ubuntuone.com/6wKtCh5iViDl0xyPSRc3LC вот тут все файлы проги.. 7.cpp главный.. когда...

Сортировка слиянием. В каком куске кода происходит сортировка и каким именно образом?
Помогите, пожалуйста, разобраться. Подскажите в каком куске кода происходит сортировка и каким...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Закрытая тема Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru