[算法天天练] - C语言实现双向链表(一)

双向链表是比较常见的,主要是在链表的基础上添加prev指针,闲话少说直接上代码吧(这个也是网上一个大神的思路,真心不错,条理清楚,逻辑缜密)

主要也是为了学习,贴上我所调试成功的代码(Linux环境下)

双向链表代码:

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3
  4 typedef struct NodeTag Node;
  5 typedef struct LinkedListTag LinkedList;
  6
  7 struct NodeTag
  8 {
  9  Node* prev;
 10  Node* next;
 11  void* data;
 12 };
 13
 14 struct LinkedListTag
 15 {
 16  Node* head;
 17  Node* tail;
 18  Node* cur;
 19  int size;
 20 };
 21
 22 // 创建一个链表 成功返回该链表的指针 否则返回NULL
 23 LinkedList* Create( void )
 24 {
 25  // 创建一个新节点
 26  LinkedList* list = (LinkedList*)malloc(sizeof(LinkedList));
 27  // 创建不成功,返回NULL
 28  if(!list) return NULL;
 29
 30  list->head = NULL;
 31  list->tail = NULL;
 32  list->cur  = NULL;
 33  list->size = 0;
 34
 35  // 初始化成功后,返回list
 36  return list;
 37 }
 38
 39 // 将元素添加到链表的末尾,成功返回链表的size,失败返回-1
 40 int AddBack(LinkedList* list, void* data)
 41 {
 42  // 创建一个新节点,创建不成功的话,返回-1
 43  Node* node = (Node *)malloc(sizeof(Node));
 44  if(!node) return -1;
 45
 46  // 为节点的数据域赋值
 47  node->data = data;
 48  // 如果为链表的末尾
 49  if(list->tail)
 50  {
 51   list->tail->next = node;         // 把新节点赋给链表末尾的下一个
 52   node->prev       = list->tail;   // 新节点的前一个等于之前的末节点
 53   node->next       = NULL;         // 新节点为末节点,把它下一个指向NULL
 54  }
 55  else // 如果不为末尾,其实就是空链表
 56  {
 57   node->next       = NULL;         // 新节点的下一个为NULL
 58   node->prev       = NULL;         // 新节点的前一个为NULL
 59   list->head       = node;         // 链表的头为新节点node
 60  }
 61  list->tail = node;                   // 链表的末尾指向node
 62
 63  return ++list->size;                 // 返回链表的size
 64 }
 65
 66 // 将元素添加到链表前端,成功返回非0,否则返回0
 67 int AddFront(LinkedList* list, void* data)
 68 {
 69  Node *node = (Node*)malloc(sizeof(Node));
 70  if(!node) return 0;
 71
 72  node->data = data;
 73  if(list->head)
 74  {
 75   list->head->prev = node;
 76   node->next       = list->head;
 77   node->prev       = NULL;
 78  }
 79  else
 80  {
 81   node->next       = NULL;
 82   node->prev       = NULL;
 83   list->tail       = node;
 84  }
 85  list->head = node;
 86
 87  return ++list->size;
 88 }
 89
 90 // 将元素从末端移除并返回该元素,如果链表为空则返回NULL
 91 void* RemoveBack(LinkedList* list)
 92 {
 93  Node* temp;
 94  void* data;
 95
 96  if(!list->size) return NULL;
 97
 98  temp = list->tail;
 99  data = list->tail->data;
100
101  if(list->head == list->tail)
102  {
103   list->head = NULL;
104   list->tail = NULL;
105   list->cur  = NULL;
106  }
107  else
108  {
109   list->tail = list->tail->prev;
110   list->tail->next = NULL;
111  }
112  --list->size;
113  free(temp);
114  return data;
115 }
116
117 // 将元素从前端移除并返回该元素,如果链表为空则返回NULL
118 void* RemoveFront(LinkedList* list)
119 {
120  Node* temp;
121  void* data;
122
123  if(!list->size) return NULL;
124
125  temp = list->head;
126  data = list->head->data;
127
128  if(list->head == list->tail)
129  {
130   list->head = NULL;
131   list->tail = NULL;
132   list->cur  = NULL;
133  }
134  else
135  {
136   list->head = list->head->next;
137   list->head->prev = NULL;
138  }
139  --list->size;
140  free(temp);
141  return data;
142 }
143
144 /* 如果当前链表为空则返回非0,否则返回0 */
145 int IsEmpty(LinkedList* list)
146 {
147     return list->size == 0;
148 }
149
150 /* 获得链表的大小(元素总个数) */
151 int Size(LinkedList* list)
152 {
153     return list->size;
154 }
155
156 /* 将当前位置移动到链表的开始 */
157 void Begin(LinkedList* list)
158 {
159     list->cur = list->head;
160 }
161
162 /* 将当前位置移动到链表的最后 */
163 void End(LinkedList* list)
164 {
165     list->cur = list->tail;
166 }
167 /* 将当前位置向后移动一个位置 */
168 void MoveNext(LinkedList* list)
169 {
170     list->cur = list->cur->next;
171 }
172
173 /* 将当前位置向后移动一个位置 */
174 void MovePrev(LinkedList* list)
175 {
176     list->cur = list->cur->prev;
177 }
178
179 /* 清空链表中所有元素 */
180 void Clear(LinkedList* list)
181 {
182  while(RemoveBack(list));
183 }
184
185 /* 销毁一个链表 */
186 void Destroy(LinkedList* list)
187 {
188  Clear(list);
189  free(list);
190 }
191
192 /* 如果当前位置之后还有元素则返回非0,否则返回0 */
193 int HasNext(LinkedList* list)
194 {
195     if (!list->cur) return 0;
196     if (list->cur == list->tail) return 1;
197     return list->cur->next != NULL;
198 }
199
200 /* 如果当前位置之前还有元素则返回非0,否则返回0 */
201 int HasPrev(LinkedList* list)
202 {
203     if (!list->cur) return 0;
204     if (list->cur == list->head) return 1;
205     return list->cur->prev != NULL;
206 }
207
208 /* 返回当前位置的元素 */
209 void* Current(LinkedList* list)
210 {
211     return list->cur->data;
212 }
213
214
215
216 // 正向打印链表
217 void Traverse(LinkedList* list)
218 {
219  for( Begin(list); HasNext(list); MoveNext(list) )
220   printf("%d ", *(int*)Current(list));
221  putchar(‘\n‘);
222 }
223
224 // 反向打印链表
225 void RTraverse(LinkedList* list)
226 {
227     for (End(list); HasPrev(list); MovePrev(list))
228         printf("%d ", *(int*)Current(list));
229     putchar(‘\n‘);
230 }
231
232
233 int main()
234 {
235  int i;
236  LinkedList* list = Create();
237
238  int array1[10];
239  int array2[10];
240
241  for(i=0; i<10; i++)
242  {
243   array1[i] = i+1;
244   array2[i] = i+100+1;
245   AddBack(list, &array1[i]);
246  }
247
248  printf("链表大小(SIZE): %d\n", Size(list));
249
250  printf("正向打印链表:\n");
251  Traverse(list);
252  printf("反向打印链表:\n");
253  RTraverse(list);
254
255  printf("添加array2[0]数\n");
256  AddBack(list, &array2[0]);
257
258  printf("链表大小(SIZE): %d\n", Size(list));
259  printf("正向打印链表:\n");
260  Traverse(list);
261  printf("反向打印链表:\n");
262  RTraverse(list);
263
264  printf("调用AddFront函数,添加array2[0]数\n");
265  AddFront(list, &array2[1]);
266
267  printf("链表大小(SIZE): %d\n", Size(list));
268  printf("正向打印链表:\n");
269  Traverse(list);
270  printf("反向打印链表:\n");
271  RTraverse(list);
272
273  printf("从末尾移除的元素是: %d\n", *(int *)RemoveBack(list));
274  printf("链表大小(SIZE): %d\n", Size(list));
275  printf("正向打印链表:\n");
276  Traverse(list);
277  printf("反向打印链表:\n");
278  RTraverse(list);
279
280  printf("从开头移除的元素是: %d\n", *(int *)RemoveFront(list));
281  printf("链表大小(SIZE): %d\n", Size(list));
282  printf("正向打印链表:\n");
283  Traverse(list);
284  printf("反向打印链表:\n");
285  RTraverse(list);
286
287  printf("清空链表,Clear(list)后\n");
288  Clear(list);
289  printf("链表大小(SIZE): %d\n", Size(list));
290
291  for(i=0; i<10; i++)
292  {
293   AddFront(list, &array2[i]);
294  }
295  printf("正向打印链表:\n");
296  Traverse(list);
297  printf("反向打印链表:\n");
298  RTraverse(list);
299
300  Destroy(list);
301  printf("销毁链表Destroy(list)\n");
302
303  return 0;
304 }

以下是执行结果:

链表大小(SIZE): 10
正向打印链表:
1 2 3 4 5 6 7 8 9 10
反向打印链表:
10 9 8 7 6 5 4 3 2 1
添加array2[0]数
链表大小(SIZE): 11
正向打印链表:
1 2 3 4 5 6 7 8 9 10 101
反向打印链表:
101 10 9 8 7 6 5 4 3 2 1
调用AddFront函数,添加array2[0]数
链表大小(SIZE): 12
正向打印链表:
102 1 2 3 4 5 6 7 8 9 10 101
反向打印链表:
101 10 9 8 7 6 5 4 3 2 1 102
从末尾移除的元素是: 101
链表大小(SIZE): 11
正向打印链表:
102 1 2 3 4 5 6 7 8 9 10
反向打印链表:
10 9 8 7 6 5 4 3 2 1 102
从开头移除的元素是: 102
链表大小(SIZE): 10
正向打印链表:
1 2 3 4 5 6 7 8 9 10
反向打印链表:
10 9 8 7 6 5 4 3 2 1
清空链表,Clear(list)后
链表大小(SIZE): 0
正向打印链表:
110 109 108 107 106 105 104 103 102 101
反向打印链表:
101 102 103 104 105 106 107 108 109 110
销毁链表Destroy(list)

在世界上,努力坚持的绝对不是自己一个人,好好努力会成功的。

时间: 2025-01-09 22:40:12

[算法天天练] - C语言实现双向链表(一)的相关文章

[算法天天练] - C语言实现约瑟夫环(2)

Linux下 #include <stdlib.h>#include <stdio.h> int main(){ int n,m,i,s = 0; printf("Entr N, M = "); scanf("%d%d", &n, &m); for(i=2; i<=n; i++) { s = (s+m) % i; } printf("\n the winner is %d\n", s+1);}

[转][算法天天练]堆与堆排序

堆排序与快速排序,归并排序一样都是时间复杂度为O(N*logN)的几种常见排序方法.学习堆排序前,先讲解下什么是数据结构中的二叉堆. 二叉堆的定义 二叉堆是完全二叉树或者是近似完全二叉树. 二叉堆满足二个特性: 1.父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值. 2.每个结点的左子树和右子树都是一个二叉堆(都是最大堆或最小堆). 当父结点的键值总是大于或等于任何一个子节点的键值时为最大堆.当父结点的键值总是小于或等于任何一个子节点的键值时为最小堆.下图展示一个最小堆: 由于其它几

[算法天天练] 归并排序

要实现归并排序递归方法: 第一步:先将原来的数据表分成排好序的子表,然后调用合并函数对子表进行归并,使之成为有序表 例如有如下向量: ⑴ ⑵ ⑶ ⑷ ⑸ ⑹ ⑺ ⑻ ⑼ ⑽ ⑾ 25, 10, 7, 19, 3, 48, 12, 17, 56, 30, 21 / 25,10,7,19,3 48,12,17,56,30,21 / \ / 25,10 7,19,3 48,12,17 56,30,21 / \ / \ / \ / 25 10 7 19,3 ... ... ... ... 归并算法的划分

[算法天天练]冒泡排序

/* * 冒泡排序 * 核心原来:每次比较两个相邻的元素,如果它们的顺序不符合要求就把它们交换 * 每趟只能确定一个数归位 * 冒泡算法的时间复杂度为O(N*N),除了名字有点儿意思外,确实效率不高 */ void BubbleSort(int array[], int length) { int tmp; for(i=0; i<length-1; i++) { for(j=0; j<length-i-1; j++)// 这里是冒泡的核心,手动画图一目了然 { if(array[j]>a

[算法天天练]快速排序

快速排序由于排序效率在同为O(N*logN)的几种排序方法中效率较高,因此经常被采用,作为面试题来考试. 该方法的基本思想是: 1.先从数列中取出一个数作为基准数. 2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边. 3.再对左右区间重复第二步,直到各区间只有一个数. 以一个数组作为示例,取区间第一个数为基准数. 0 1 2 3 4 5 6 7 8 9 72 6 57 88 60 42 83 73 48 85 初始时,i = 0;  j = 9;   X = a[i]

[算法天天练]选择排序

选择排序 #include <iostream> using namespace std; void SelectSort(int data[], int ilen) { int temp; int iIndex = 0; for(int i=0; i<ilen-1; i++) { iIndex = i; for(int j=i+1; j<ilen; j++) { if(data[j]<data[iIndex]) { iIndex = j; } } if(iIndex !=

[算法天天练]插入排序算法

#include <stdio.h> void show(int arr[], int length) { for(int i=0; i<length; i++) { printf("%d ", arr[i]); } printf("\n"); } void s_insert(int arr[], int length) { if(length < 0) return ; for(int i=1; i<length; i++) { in

[算法天天练]选择排序法

#include<stdio.h> void show(int arr[], int length) { for(int i=0; i<length; i++) { printf("%d ", arr[i]); } printf("\n"); } void _swap(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp; } void SelectSort(int arr[], int length)

[算法天天练]递归查找和非递归查找

//Data为要查找的数组,x为待查找数据值,beg为查找范围起始,last为查找范围终止 //非递归法 int BiSearch(int data[], const int x, int beg, int last) { int mid;//中间位置 if (beg > last) { return -1; } while(beg <= last) { mid = (beg + last) / 2; if (x == data[mid] ) { return mid; } else if (