C/C++語言實現單鏈表(帶頭結點)


徹底理解鏈表中為何使用二級指針或者一級指針的引用

數據結構之鏈表-鏈表實現及常用操作(C++篇)

  C語言實現單鏈表,主要功能為空鏈表創建,鏈表初始化(頭插法),鏈表元素讀取,按位置插入,(有序鏈表)按值插入,按位置刪除,按值刪除,清空鏈表,銷毀鏈表。

  關鍵思路:(1)將結點創建結構體;(2)鏈表中添加頭結點,以便統一操作;(3)使用結點一級指針和二級指針的異同點;(4)鏈表的最小操作單位是結點;(5)操作的起始位置是頭結點還是第一個結點,及起始索引是0還是1. 

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <time.h>
  4 
  5 //涉及到結構體自引用
  6 typedef struct Node{
  7     int data;
  8     struct Node *next;
  9 }Node;
 10 
 11 //創建空鏈表
 12 //必須用二級指針,涉及到頭指針的創建
 13 int iniList(Node **List){
 14     *List = (Node *)malloc(sizeof(Node));
 15     if (NULL == *List){
 16         return 0;
 17     }
 18 
 19     (*List)->next = NULL; //創建頭結點
 20     return 1;
 21 }
 22 
 23 //初始化鏈表(頭插法)
 24 //必須二級指針
 25 int iniListHead(Node **List, int n){
 26     *List = (Node *)malloc(sizeof(Node));
 27     if (NULL == *List){
 28         return 0;
 29     }
 30     (*List)->next = NULL;
 31     srand(time(0));
 32 
 33     int i = 0;
 34     while (i < n){
 35         Node *tmpNode = (Node *)malloc(sizeof(Node));
 36         if (NULL == tmpNode){
 37             return 0;
 38         }
 39         tmpNode->data = rand() % 100 + 1;
 40         tmpNode->next = (*List)->next;
 41         (*List)->next = tmpNode;
 42         
 43 
 44         ++i;
 45     }
 46     return 1;
 47 }
 48 
 49 //初始化鏈表(尾插法)
 50 //必須二級指針
 51 //需要借助輔助變量pCurrent,將每次尾插的新元素當做當前元素
 52 int iniListTail(Node **List, int n){
 53     *List = (Node *)malloc(sizeof(Node));
 54     if (NULL == *List){
 55         return 0;
 56     }
 57     (*List)->next = NULL;
 58     srand(time(0));
 59 
 60     Node *pCurrent = *List;
 61 
 62     int i = 0;
 63     while (i < n){
 64         Node *tmpNode = (Node *)malloc(sizeof(Node));
 65         if (NULL == tmpNode){
 66             return 0;
 67         }
 68         tmpNode->data = rand() % 100 + 1;
 69         tmpNode->next = NULL;
 70         pCurrent->next = tmpNode;
 71         pCurrent = tmpNode;
 72 
 73         ++i;
 74     }
 75     return 1;
 76 }
 77 
 78 //清空鏈表(不刪除頭結點)
 79 //一級,二級指針均可
 80 //首先找到鏈表地址,然后移動至表尾
 81 //判斷條件為指針域是否為空,即到達結尾
 82 int deleteList(Node *List){
 83 
 84     //這種方法無法刪除尾結點
 85     //Node *p= List;
 86     //Node *q = NULL;
 87     //
 88     //while (p->next){
 89     //    q = p;
 90     //    free(p);
 91     //    p = q->next;
 92     //}
 93 
 94     Node *p = List->next;
 95     Node *q = NULL;
 96 
 97     while (p){
 98         q = p->next;
 99         free(p);
100         p = q;
101     }
102 
103     List->next = NULL;
104     return 1;
105 }
106 
107 //銷毀鏈表
108 //必須使用二級指針,銷毀頭結點和頭指針
109 //最后將鏈表頭指針置空
110 int desrotyList(Node **List){
111 
112     Node *p = *List;
113     Node *q = NULL;
114 
115     //如果為空鏈表,直接刪除頭結點
116     //如果不是空鏈表,從頭結點開始刪除
117     while (p){
118         q = p->next;
119         free(p);
120         p = q;
121     }
122     (*List) = NULL;
123 
124     //下面是從第一個結點開始刪除
125     //最后釋放掉頭結點
126     //Node *p = (*List)->next;
127     //Node *q = NULL;
128     //
129     //while (p){
130     //    q = p->next;
131     //    free(p);
132     //    p = q;
133     //}
134     //free(*List);
135     //(*List) = NULL;
136 
137     return 1;
138 }
139 
140 //鏈表獲取元素
141 //一級,二級指針均可
142 //頭結點無意義,從第一個結點開始遍歷,i從1開始
143 //每次都指向下一結點,到pos-1即可
144 int getList(Node *List, int pos, int *element){
145     Node *p = List->next;
146     
147     int i = 1;
148     while (p && i < pos){
149         p = p->next;
150         ++i;
151     }
152     *element = p->data;
153     return 1;
154 }
155 
156 //鏈表按位置插入
157 //一級,二級指針均可
158 //從頭結點開始,有可能插入在第一個位置,遍歷從1開始
159 int insertListPos(Node *List, int pos, int value){
160     Node *p = List;
161 
162     int i = 1;
163     while (p && i < pos){
164         p = p->next;
165         ++i;
166     }
167     Node *tmpNode = (Node *)malloc(sizeof(Node));
168     tmpNode->data = value;
169     tmpNode->next = p->next;
170     p->next = tmpNode;
171     return 1;
172 }
173 
174 //有序鏈表,按值插入
175 //一二級指針均可
176 int insertListValue(Node *List, int value){
177     Node *pCur = List->next;
178     Node *pPer = List;
179     while (pCur && pCur->data < value){
180         pPer = pCur;
181         pCur = pCur->next;
182     }
183 
184     Node *tmpNode = (Node *)malloc(sizeof(Node));
185     if (NULL == tmpNode){
186         return 0;
187     }
188     tmpNode->data = value;
189     tmpNode->next = pPer->next;
190     pPer->next = tmpNode;
191     return 1;
192 }
193 
194 //鏈表按位置刪除
195 //一二級指針均可
196 //記得釋放結點內存
197 //如果刪除第一個結點,需要操縱頭結點
198 int deleteListPos(Node *List, int pos){
199     Node *p = List;
200 
201     int i = 1;
202     while (p && i < pos){
203         p = p->next;
204         ++i;
205     }
206     if (NULL == p->next)
207         return 0;
208 
209     Node *tmpNode = p->next;
210     p->next = p->next->next;
211     
212     free(tmpNode);
213     return 1;
214 }
215 
216 //鏈表按值刪除元素
217 //一二級指針均可
218 //從第一個結點開始
219 int deleteListValue(Node *List, int value){
220     Node *pCur = List->next;
221     Node *pPer = List;
222 
223     while (pCur && pCur->data != value){
224         pPer = pCur;
225         pCur = pCur->next;
226     }
227     if (pCur == NULL){ //空鏈表不刪除任何結點
228         return 0;
229     }
230     else{
231         pPer->next = pCur->next;
232         free(pCur);
233     }
234     return 1;
235 }
236 
237 int main(){
238 
239     Node *testList = NULL;
240     iniList(&testList);
241     //iniListHead(&testList, 3);
242     //iniListTail(&testList, 3);
243     
244 
245     insertListPos(testList, 1, 2);
246     insertListPos(testList, 2, 4);
247     insertListPos(testList, 3, 5);
248     insertListPos(testList, 4, 10);
249     //insertListPos(testList, 1, 1);
250     insertListValue(testList, 1);
251 
252     //deleteListPos(testList, 1);
253     //deleteListValue(testList, 4);
254 
255     //deleteList(testList);
256 
257     //printf("%d\n", testList);
258     //desrotyList(&testList);
259     //printf("%d\n", testList);
260 
261     Node * tmpNode = testList->next;
262     while (tmpNode){
263         printf("%d\n", tmpNode->data);
264         tmpNode = tmpNode->next;
265     }
266 
267     printf("----------------------\n");
268 
269     int a = 0;
270     getList(testList, 2, &a);
271     printf("%d\n", a);
272 
273     system("pause");
274 
275     return 0;
276 }
C語言完整代碼

  通過C++實現C語言的鏈表,主要區別:(1)struct可以不通過typedef,直接使用Node;(2)將malloc和free更換為new和delete

  1 #include<iostream>
  2 #include<ctime>
  3 
  4 using namespace std;
  5 
  6 struct Node{
  7     int data;
  8     Node *next;
  9 };
 10 
 11 //創建空鏈表
 12 //必須用二級指針,涉及到頭指針的創建
 13 int iniList(Node **List){
 14     *List = new Node;
 15 
 16     (*List)->next = NULL; //創建頭結點
 17     return 1;
 18 }
 19 
 20 //初始化鏈表(頭插法)
 21 //必須二級指針
 22 int iniListHead(Node **List, int n){
 23     *List = new Node;
 24 
 25     (*List)->next = NULL;
 26     srand(time(0));
 27 
 28     int i = 0;
 29     while (i < n){
 30         Node *tmpNode = new Node;
 31 
 32         tmpNode->data = rand() % 100 + 1;
 33         tmpNode->next = (*List)->next;
 34         (*List)->next = tmpNode;
 35 
 36         ++i;
 37     }
 38     return 1;
 39 }
 40 
 41 //初始化鏈表(尾插法)
 42 //必須二級指針
 43 //需要借助輔助變量pCurrent,將每次尾插的新元素當做當前元素
 44 int iniListTail(Node **List, int n){
 45     *List = new Node;
 46 
 47     (*List)->next = NULL;
 48     srand(time(0));
 49 
 50     Node *pCurrent = *List;
 51 
 52     int i = 0;
 53     while (i < n){
 54         Node *tmpNode = new Node;
 55 
 56         tmpNode->data = rand() % 100 + 1;
 57         tmpNode->next = NULL;
 58         pCurrent->next = tmpNode;
 59         pCurrent = tmpNode;
 60 
 61         ++i;
 62     }
 63     return 1;
 64 }
 65 
 66 //清空鏈表(不刪除頭結點)
 67 //一級,二級指針均可
 68 //首先找到鏈表地址,然后移動至表尾
 69 //判斷條件為指針域是否為空,即到達結尾
 70 int deleteList(Node *List){
 71 
 72     //這種方法無法刪除尾結點
 73     //Node *p= List;
 74     //Node *q = NULL;
 75     //
 76     //while (p->next){
 77     //    q = p;
 78     //    free(p);
 79     //    p = q->next;
 80     //}
 81 
 82     Node *p = List->next;
 83     Node *q = NULL;
 84 
 85     while (p){
 86         q = p->next;
 87         delete p;
 88         p = q;
 89     }
 90 
 91     List->next = NULL;
 92     return 1;
 93 }
 94 
 95 //銷毀鏈表
 96 //必須使用二級指針,銷毀頭結點和頭指針
 97 //最后將鏈表頭指針置空
 98 int desrotyList(Node **List){
 99 
100     Node *p = *List;
101     Node *q = NULL;
102 
103     //如果為空鏈表,直接刪除頭結點
104     //如果不是空鏈表,從頭結點開始刪除
105     while (p){
106         q = p->next;
107         delete p;
108         p = q;
109     }
110     (*List) = NULL;
111 
112     //下面是從第一個結點開始刪除
113     //最后釋放掉頭結點
114     //Node *p = (*List)->next;
115     //Node *q = NULL;
116     //
117     //while (p){
118     //    q = p->next;
119     //    free(p);
120     //    p = q;
121     //}
122     //free(*List);
123     //(*List) = NULL;
124 
125     return 1;
126 }
127 
128 //鏈表獲取元素
129 //一級,二級指針均可
130 //頭結點無意義,從第一個結點開始遍歷,i從1開始
131 //每次都指向下一結點,到pos-1即可
132 int getList(Node *List, int pos, int *element){
133     Node *p = List->next;
134 
135     int i = 1;
136     while (p && i < pos){
137         p = p->next;
138         ++i;
139     }
140     *element = p->data;
141     return 1;
142 }
143 
144 //鏈表按位置插入
145 //一級,二級指針均可
146 //從頭結點開始,有可能插入在第一個位置,遍歷從1開始
147 int insertListPos(Node *List, int pos, int value){
148     Node *p = List;
149 
150     int i = 1;
151     while (p && i < pos){
152         p = p->next;
153         ++i;
154     }
155     Node *tmpNode = new Node;
156     tmpNode->data = value;
157     tmpNode->next = p->next;
158     p->next = tmpNode;
159     return 1;
160 }
161 
162 //有序鏈表,按值插入
163 //一二級指針均可
164 int insertListValue(Node *List, int value){
165     Node *pCur = List->next;
166     Node *pPer = NULL;
167     while (pCur && pCur->data < value){
168         pPer = pCur;
169         pCur = pCur->next;
170     }
171 
172     Node *tmpNode = new Node;
173     if (NULL == tmpNode){
174         return 0;
175     }
176     tmpNode->data = value;
177     tmpNode->next = pPer->next;
178     pPer->next = tmpNode;
179     return 1;
180 }
181 
182 //鏈表按位置刪除
183 //一二級指針均可
184 //記得釋放結點內存
185 //如果刪除第一個結點,需要操縱頭結點
186 int deleteListPos(Node *List, int pos){
187     Node *p = List;
188 
189     int i = 1;
190     while (p && i < pos){
191         p = p->next;
192         ++i;
193     }
194     Node *tmpNode = p->next;
195     p->next = p->next->next;
196 
197     delete tmpNode;
198     return 1;
199 }
200 
201 //鏈表按值刪除元素
202 //一二級指針均可
203 //從第一個結點開始
204 int deleteListValue(Node *List, int value){
205     Node *pCur = List->next;
206     Node *pPer = List;
207 
208     while (pCur && pCur->data != value){
209         pPer = pCur;
210         pCur = pCur->next;
211     }
212     if (pCur == NULL){
213         return 0;
214     }
215     else{
216         pPer->next = pCur->next;
217         delete pCur;
218     }
219     return 1;
220 }
C++完整代碼

結點結構體

  將結點創建為結構體,其中包含數據域和指針域成員,指針域涉及結構體自引用。指針域成員之所以為結點類指針,一是為了編譯時能明確結構體大小,二是為了指向下一個結點,因此初始化時不用開辟內存,只需要賦值為NULL即可。當然了,開辟內存也是可以的,這樣在刪除結點,及清空鏈表時需要記得將其釋放,多此一舉,不提倡。

1 //涉及到結構體自引用
2 typedef struct Node{
3     int data;
4     struct Node *next;
5 }Node;

 

空鏈表創建(二級指針)

  空鏈表創建時,建議創建頭結點。在插入和刪除第一個位置的元素時,需要用結點的二級指針移動頭指針,但普通結點的插入和刪除並不需要移動頭指針。為了便於操作的統一(指插入和刪除操作),這里先創建頭結點。

  另外,在創建空鏈表時,需要傳入二級指針,主要是為了操作頭指針,只要涉及操作頭指針的都需要使用二級指針。

 1 //創建空鏈表
 2 //必須用二級指針,涉及到頭指針的創建
 3 int iniList(Node **List){
 4     *List = (Node *)malloc(sizeof(Node));
 5     if (NULL == *List){
 6         return 0;
 7     }
 8 
 9     (*List)->next = NULL; //創建頭結點,將其指針域置空
10     return 1;
11 }

 

鏈表初始化(二級指針)

  鏈表初始化,即是創建空鏈表,並對鏈表中的n個元素進行賦值。一般來說,有頭插法、尾插法兩種,頭插法是在頭結點后插入,尾插法是在鏈表尾插入。

  頭插法

  頭插法一般思路:(1)創建帶頭結點的空鏈表;(2)創建新結點,對新結點賦值;(3)新結點指向頭結點的下一個結點,頭結點指向新結點。

 1 //初始化鏈表(頭插法)
 2 //必須二級指針
 3 int iniListHead(Node **List, int n){
 4     *List = (Node *)malloc(sizeof(Node));
 5     if (NULL == *List){
 6         return 0;
 7     }
 8     (*List)->next = NULL;
 9     srand(time(0));
10 
11     int i = 0;
12     while (i < n){
13         Node *tmpNode = (Node *)malloc(sizeof(Node));
14         if (NULL == tmpNode){
15             return 0;
16         }
17         tmpNode->data = rand() % 100 + 1;
18         tmpNode->next = (*List)->next;
19         (*List)->next = tmpNode;
20         
21 
22         ++i;
23     }
24     return 1;
25 }

 

  尾插法

  尾插法一般思路:(1)創建帶頭結點的空鏈表;(2)創建新結點,對新結點數據域賦值,指針域置空;(3)建立臨時變量指向頭結點,頭結點指向新結點;(4)將臨時變量往后移動,指向新結點。

 1 //初始化鏈表(尾插法)
 2 //必須二級指針
 3 //需要借助輔助變量pCurrent,將每次尾插的新元素當做當前元素
 4 int iniListTail(Node **List, int n){
 5     *List = (Node *)malloc(sizeof(Node));
 6     if (NULL == *List){
 7         return 0;
 8     }
 9     (*List)->next = NULL;
10     srand(time(0));
11 
12     Node *pCurrent = *List;
13 
14     int i = 0;
15     while (i < n){
16         Node *tmpNode = (Node *)malloc(sizeof(Node));
17         if (NULL == tmpNode){
18             return 0;
19         }
20         tmpNode->data = rand() % 100 + 1;
21         tmpNode->next = NULL;
22         pCurrent->next = tmpNode;
23         pCurrent = tmpNode;
24 
25         ++i;
26     }
27     return 1;
28 }

 

鏈表元素讀取(一、二級指針)

  鏈表元素讀取,需要涉及到索引位置。我們知道頭結點的數據域沒有意義,因此起始位置從第一個結點開始,遍歷值從1開始,到pos-1為止,此時p指向pos結點。

 1 //鏈表獲取元素
 2 //一級,二級指針均可
 3 //頭結點無意義,從第一個結點開始遍歷,i從1開始
 4 //每次都指向下一結點,到pos-1即可
 5 int getList(Node *List, int pos, int *element){
 6     Node *p = List->next;
 7     
 8     int i = 1;
 9     while (p && i < pos){
10         p = p->next;
11         ++i;
12     }
13     *element = p->data;
14     return 1;
15 }

 

按位置插入(一、二級指針)

  插入時,需要考慮任意位置,由於頭結點的設立,我們不需要單獨考慮第一個位置的結點插入。

  一般思路:(1)起始位置從頭結點開始,遍歷值從1開始,到pos-1為止,此時指向pos-1結點;(2)創建新結點,給新結點數據域賦值;(3)新結點指向pos位置的下一結點,pos位置指向新結點。

 1 //鏈表按位置插入
 2 //一級,二級指針均可
 3 //從頭結點開始,有可能插入在第一個位置,遍歷從1開始
 4 int insertListPos(Node *List, int pos, int value){
 5     Node *p = List;
 6 
 7     int i = 1;
 8     while (p && i < pos){
 9         p = p->next;
10         ++i;
11     }
12     Node *tmpNode = (Node *)malloc(sizeof(Node));
13     tmpNode->data = value;
14     tmpNode->next = p->next;
15     p->next = tmpNode;
16     return 1;
17 }

 

有序鏈表按值插入(一、二級指針)

  有序鏈表中涉及到按值插入,按值刪除時,需要建立兩個臨時變量,不同於按位置插入,我們可以在pos前一個停止遍歷,按值插入,我們需要遍歷找到插入位置,操作插入位置的前一個結點。

  一般思路:(1)從第一個結點開始遍歷;(2)創建per變量標記當前結點的前一結點;(3)創建新結點,操作per結點;(4)常規插入操作。

 1 //有序鏈表,按值插入
 2 //一二級指針均可
 3 int insertListValue(Node *List, int value){
 4     Node *pCur = List->next;
 5     Node *pPer = NULL;
 6     while (pCur && pCur->data < value){
 7         pPer = pCur;
 8         pCur = pCur->next;
 9     }
10 
11     Node *tmpNode = (Node *)malloc(sizeof(Node));
12     if (NULL == tmpNode){
13         return 0;
14     }
15     tmpNode->data = value;
16     tmpNode->next = pPer->next;
17     pPer->next = tmpNode;
18     return 1;
19 }

 

按位置刪除(一、二級指針)

  刪除時,由於頭結點的設立,因此不需要特殊考慮第一個位置的操作和頭指針的移動。需要設置臨時變量是釋放p->next后,后面還會使用p->next的結點,這樣會造成訪問出錯。

  一般思路:(1)起始位置從頭結點開始,遍歷從1開始,到pos-1為止,此時指向pos-1結點;(2)創建臨時變量,指向pos結點;(3)將pos-1結點指向pos的下一結點,釋放掉pos結點。

 1 //鏈表按位置刪除
 2 //一二級指針均可
 3 //記得釋放結點內存
 4 //如果刪除第一個結點,需要操縱頭結點
 5 int deleteListPos(Node *List, int pos){
 6     Node *p = List;
 7 
 8     int i = 1;
 9     while (p && i < pos){
10         p = p->next;
11         ++i;
12     }
13     Node *tmpNode = p->next;
14     p->next = p->next->next;
15     
16     free(tmpNode);
17     return 1;
18 }

 

按值刪除(一、二級指針)

  按值刪除與按值插入一樣,需要兩個臨時變量。

  一般思路:(1)起始位置從第一個結點開始,(2)設立per結點指針保存當前結點的上一結點;(3)常規刪除操作。

 1 //鏈表按值刪除元素
 2 //一二級指針均可
 3 //從第一個結點開始
 4 int deleteListValue(Node *List, int value){
 5     Node *pCur = List->next;
 6     Node *pPer = List;
 7 
 8     while (pCur && pCur->data != value){
 9         pPer = pCur;
10         pCur = pCur->next;
11     }
12     if (pCur == NULL){ //空鏈表不刪除任何結點
13         return 0;
14     }
15     else{
16         pPer->next = pCur->next;
17         free(pCur);
18     }
19     return 1;
20 }

 

清空鏈表(一、二級指針)

  清空鏈表,只是將鏈表中的結點刪除,並釋放掉結點空間,保留頭結點,並將頭結點的指針域置空。

  一般思路:(1)起始位置從第一個結點開始;(2)設置臨時變量q指向當前結點p的下一結點,釋放掉當前結點p,將臨時變量q賦給p;(3)最后將頭結點的指針置空。

 1 //清空鏈表(不刪除頭結點)
 2 //一級,二級指針均可
 3 //首先找到鏈表地址,然后移動至表尾
 4 //判斷條件為指針域是否為空,即到達結尾
 5 int deleteList(Node *List){
 6 
 7     //這種方法無法刪除尾結點,當p->next是尾結點上一節點時,走一遍程序
 8     //Node *p= List;
 9     //Node *q = NULL;
10     //
11     //while (p->next){
12     //    q = p;
13     //    free(p);
14     //    p = q->next;
15     //}
16 
17     Node *p = List->next;
18     Node *q = NULL;
19 
20     while (p){
21         q = p->next;
22         free(p);
23         p = q;
24     }
25 
26     List->next = NULL;
27     return 1;
28 }

 

銷毀鏈表(二級指針)

  銷毀鏈表,是在清空鏈表的基礎上,刪除頭結點,將頭指針置空,涉及到頭指針的操作,需要二級指針。

  思路一:(1)與清空鏈表操作相同,從第一個結點開始刪除;(2)最后釋放掉頭結點,將頭指針置空

  思路二:(1)起始位置從頭結點開始刪除;(2)將頭指針置空

 1 //銷毀鏈表
 2 //必須使用二級指針,銷毀頭結點和頭指針
 3 //最后將鏈表頭指針置空
 4 int desrotyList(Node **List){
 5 
 6     Node *p = *List;
 7     Node *q = NULL;
 8 
 9     //如果為空鏈表,直接刪除頭結點
10     //如果不是空鏈表,從頭結點開始刪除
11     while (p){
12         q = p->next;
13         free(p);
14         p = q;
15     }
16     (*List) = NULL;
17 
18     //下面是從第一個結點開始刪除
19     //最后釋放掉頭結點
20     //Node *p = (*List)->next;
21     //Node *q = NULL;
22     //
23     //while (p){
24     //    q = p->next;
25     //    free(p);
26     //    p = q;
27     //}
28     //free(*List);
29     //(*List) = NULL;
30 
31     return 1;
32 }

 二級指針和一級指針插入解析

 

-------------------------------------------------------------------------------------------------------------

如果上面的資料對你有啟發,麻煩點個推薦,讓更多人的人看到哦。

關注公眾號【兩猿社】,懂點互聯網,懂點IC的程序猿,帶你豐富項目經驗哦。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM