• C语言对单链表所有操作与一些相关面试题


    目录

    单链表的特性

    单链表的所有操作

    定义一个单链表

    创建一个链表头

    插入数据(头插法)

    插入数据(尾插法)

    查找节点

    修改数据节点

    删除节点

    打印数据

    销毁链表

    翻转链表

    打印链表长度

    冒泡排序

    快排

    堆排

    查找倒数第K个节点(双指针法)

    完整测试代码


    单链表的特性

    单链表是一种线性数据结构,它由一系列的节点组成,每个节点包含一个数据域和一个指向下一个节点的指针域。单链表的特性有:

    • 单链表的长度是可变的,可以动态地插入和删除节点。
    • 单链表的访问是顺序的,要访问某个节点,必须从头节点开始遍历,直到找到该节点或者到达链表尾部。
    • 单链表不需要连续的内存空间,可以利用零散的空间存储数据。
    • 单链表的优势是:
      • 插入和删除操作比较简单,只需要修改指针域即可,不需要移动其他节点。
      • 单链表可以实现一些特殊的功能,如栈、队列、循环链表等。
    • 单链表的劣势是:
      • 访问操作比较慢,需要遍历整个链表,时间复杂度为O(n)。
      • 单链表需要额外的空间存储指针域,增加了空间开销。
      • 单链表容易产生内存碎片,如果频繁地插入和删除节点,可能导致内存不连续。

    单链表的所有操作

    定义一个单链表
    1. // 声明并定义个单链表结构体
    2. typedef struct _ListNode
    3. {
    4. int val; //数据 成员变量
    5. struct _ListNode * next;//结构体调用自己的类型
    6. }ListNode;
    创建一个链表头
    1. void listCreate(ListNode *node)
    2. {
    3. //初始化链表内数据
    4. node->val = -1;
    5. node->next = NULL;
    6. }
    插入数据(头插法)
    1. void listInsert(ListNode *node, int data)
    2. {
    3. // 创建一个节点,并申请内存
    4. ListNode *t_node = (ListNode *)malloc(sizeof(ListNode));
    5. // 节点内容赋值
    6. t_node->val = data;
    7. // 头插法,新数据在前
    8. t_node->next = node->next;
    9. node->next = t_node;
    10. }
    插入数据(尾插法)
    1. void listTailInsert(ListNode *node, int data)
    2. {
    3. // 创建一个节点
    4. ListNode *t_node = (ListNode*)malloc(sizeof(ListNode));
    5. // 节点内容赋值
    6. t_node->val = data;
    7. t_node->next = NULL;
    8. // 声明一个尾节点
    9. ListNode* t_tail = node;
    10. // 获取最后一个节点
    11. while(t_tail->next != NULL)
    12. {
    13. // 后移
    14. t_tail = t_tail->next;
    15. }
    16. //添加节点
    17. t_tail->next = t_node;
    18. }
    查找节点
    1. ListNode* listFind(ListNode *node, int data)
    2. {
    3. //申明一个空节点
    4. ListNode *t_node = NULL;
    5. //遍历链表
    6. ListNode *t_temp;
    7. for(t_temp = node->next; t_temp != NULL; t_temp = t_temp->next)
    8. {
    9. //如果找到该节点
    10. if(t_temp->val == data)
    11. {
    12. t_node = t_temp;
    13. //跳出循环
    14. break;
    15. }
    16. }
    17. return t_node;
    18. }
    修改数据节点
    1. void listModify(ListNode *node, int oldData, int newData)
    2. {
    3. // 查找值是否存在
    4. ListNode *t_node = listFind(node, oldData);
    5. // 判断值是否存在
    6. if(t_node == NULL)
    7. {
    8. printf("该值不存在\n");
    9. return;
    10. }
    11. t_node->val = newData;
    12. }
    删除节点
    1. void listDelete(ListNode *node, int data)
    2. {
    3. // 查找是否存在改制的数据
    4. ListNode *t_node = listFind(node, data);
    5. // 如果该值对应的节点不存在
    6. if(NULL == t_node)
    7. {
    8. printf("该值不存在\n");
    9. return;
    10. }
    11. // 求出被删节点的前一个节点
    12. ListNode *t_prev = node;
    13. // 遍历链表
    14. while(t_prev->next != t_node)
    15. {
    16. t_prev = t_prev->next;
    17. }
    18. // 前一个节点的next指向被删除节点的下一个节点
    19. t_prev->next = t_node->next;
    20. // 释放内存
    21. free(t_node);
    22. // 指针置空
    23. t_node = NULL;
    24. }
    打印数据
    1. void listDisplay(ListNode *node)
    2. {
    3. // 遍历链表
    4. ListNode *t_temp;
    5. for(t_temp = node->next; t_temp != NULL; t_temp = t_temp->next)
    6. {
    7. printf("%d ",t_temp->val);
    8. }
    9. printf("\n");
    10. }
    销毁链表
    1. void listDestroy(ListNode *node)
    2. {
    3. // 遍历链表
    4. ListNode *t_temp = node->next;
    5. while(t_temp != NULL)
    6. {
    7. // 先将当前节点保存
    8. ListNode *t_node = t_temp;
    9. // 移动到下一各节点
    10. t_temp = t_temp->next;
    11. // 释放保存内容的节点
    12. free(t_node);
    13. }
    14. }
    翻转链表
    1. void listReverse(ListNode *node)
    2. {
    3. ListNode * head = NULL, *now = NULL, *temp = NULL;
    4. head = node->next;
    5. // head是来保存我们翻转以后链表中的头节点的
    6. now = head->next;
    7. // now用来保存我们当前待处理的节点
    8. head->next = NULL;
    9. // 一定要置为NULL,否则可能导致循环
    10. while(now)
    11. {
    12. temp = now->next; // 利用一个临时指针来保存下一个待处理的节点
    13. now->next = head; // 将当前节点插入到逆序节点的第一个节点之前,并更改head指向
    14. head = now;
    15. node->next = head; // 使链表头指针指向逆序后的第一个节点
    16. now = temp; // 更新链表到下一个待处理的节点
    17. }
    18. }
    打印链表长度
    1. int listLength(ListNode *node)
    2. {
    3. ListNode *t_temp;
    4. int t_length = 0;
    5. for(t_temp = node->next; t_temp != NULL; t_temp = t_temp->next)
    6. {
    7. t_length++;
    8. }
    9. return t_length;
    10. }
    冒泡排序
    1. void listBubbleSort(ListNode *node)
    2. {
    3. int t_length = listLength(node);
    4. int i,j;
    5. ListNode *t_temp;
    6. for(i = 0; i < t_length; i++)
    7. {
    8. t_temp = node->next;
    9. for(j = 0;j < t_length - i - 1; j++)
    10. {
    11. if(t_temp->val > t_temp->next->val)
    12. {
    13. int t_data = t_temp->val;
    14. t_temp->val = t_temp->next->val;
    15. t_temp->next->val = t_data;
    16. }
    17. t_temp = t_temp->next;
    18. }
    19. }
    20. }
    快排
    1. void quickSort(struct _ListNode *head, struct _ListNode *tail) {
    2. // 如果链表为空或只有一个节点,直接返回
    3. if (head == NULL || head == tail) return;
    4. // 定义两个指针p和q,用于分割链表
    5. struct _ListNode *p = head, *q = head->next;
    6. // 选取第一个节点作为基准值
    7. int pivot = head->val;
    8. // 遍历链表,将小于基准值的节点放到p的后面
    9. while (q != tail->next) {
    10. if (q->val < pivot) {
    11. p = p->next;
    12. // 交换p和q指向的节点的值
    13. int temp = p->val;
    14. p->val = q->val;
    15. q->val = temp;
    16. }
    17. q = q->next;
    18. }
    19. // 交换head和p指向的节点的值,使得p指向的节点为基准值
    20. int temp = head->val;
    21. head->val = p->val;
    22. p->val = temp;
    23. // 对左右两部分递归进行快速排序
    24. quickSort(head, p);
    25. quickSort(p->next, tail);
    26. }
    堆排
    // 待实现
    查找倒数第K个节点(双指针法)
    1. ListNode* listFindKthToTail(ListNode *node, int k)
    2. {
    3. // 超过长度直接返回空
    4. if(node == NULL || k >= listLength(node))return NULL;
    5. ListNode *first = node, *second = node;
    6. for(int i = 0; i < k; i++){
    7. first = first->next;
    8. }
    9. while (first)
    10. {
    11. first = first->next;
    12. second = second->next;
    13. }
    14. return second;
    15. }

    完整测试代码

    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. // 声明并定义个单链表结构体
    4. typedef struct _ListNode
    5. {
    6. int val; //数据 成员变量
    7. struct _ListNode * next;//结构体调用自己的类型
    8. }ListNode;
    9. /**
    10. * 创建链表
    11. */
    12. void listCreate(ListNode *node)
    13. {
    14. //初始化链表内数据
    15. node->val = -1;
    16. node->next = NULL;
    17. }
    18. /**
    19. * 插入数据,头插法
    20. */
    21. void listInsert(ListNode *node, int data)
    22. {
    23. // 创建一个节点,并申请内存
    24. ListNode *t_node = (ListNode *)malloc(sizeof(ListNode));
    25. // 节点内容赋值
    26. t_node->val = data;
    27. // 头插法,新数据在前
    28. t_node->next = node->next;
    29. node->next = t_node;
    30. }
    31. /**
    32. * 插入数据,尾插法
    33. */
    34. void listTailInsert(ListNode *node, int data)
    35. {
    36. // 创建一个节点
    37. ListNode *t_node = (ListNode*)malloc(sizeof(ListNode));
    38. // 节点内容赋值
    39. t_node->val = data;
    40. t_node->next = NULL;
    41. // 声明一个尾节点
    42. ListNode* t_tail = node;
    43. // 获取最后一个节点
    44. while(t_tail->next != NULL)
    45. {
    46. // 后移
    47. t_tail = t_tail->next;
    48. }
    49. //添加节点
    50. t_tail->next = t_node;
    51. }
    52. /**
    53. * 查找数据
    54. */
    55. ListNode* listFind(ListNode *node, int data)
    56. {
    57. //申明一个空节点
    58. ListNode *t_node = NULL;
    59. //遍历链表
    60. ListNode *t_temp;
    61. for(t_temp = node->next; t_temp != NULL; t_temp = t_temp->next)
    62. {
    63. //如果找到该节点
    64. if(t_temp->val == data)
    65. {
    66. t_node = t_temp;
    67. //跳出循环
    68. break;
    69. }
    70. }
    71. return t_node;
    72. }
    73. /**
    74. * 修改数据
    75. */
    76. void listModify(ListNode *node, int oldData, int newData)
    77. {
    78. // 查找值是否存在
    79. ListNode *t_node = listFind(node, oldData);
    80. // 判断值是否存在
    81. if(t_node == NULL)
    82. {
    83. printf("该值不存在\n");
    84. return;
    85. }
    86. t_node->val = newData;
    87. }
    88. /**
    89. * 删除数据
    90. */
    91. void listDelete(ListNode *node, int data)
    92. {
    93. // 查找是否存在改制的数据
    94. ListNode *t_node = listFind(node, data);
    95. // 如果该值对应的节点不存在
    96. if(NULL == t_node)
    97. {
    98. printf("该值不存在\n");
    99. return;
    100. }
    101. // 求出被删节点的前一个节点
    102. ListNode *t_prev = node;
    103. // 遍历链表
    104. while(t_prev->next != t_node)
    105. {
    106. t_prev = t_prev->next;
    107. }
    108. // 前一个节点的next指向被删除节点的下一个节点
    109. t_prev->next = t_node->next;
    110. // 释放内存
    111. free(t_node);
    112. // 指针置空
    113. t_node = NULL;
    114. }
    115. /**
    116. * 打印数据
    117. */
    118. void listDisplay(ListNode *node)
    119. {
    120. // 遍历链表
    121. ListNode *t_temp;
    122. for(t_temp = node->next; t_temp != NULL; t_temp = t_temp->next)
    123. {
    124. printf("%d ",t_temp->val);
    125. }
    126. printf("\n");
    127. }
    128. /**
    129. * 销毁链表
    130. */
    131. void listDestroy(ListNode *node)
    132. {
    133. // 遍历链表
    134. ListNode *t_temp = node->next;
    135. while(t_temp != NULL)
    136. {
    137. // 先将当前节点保存
    138. ListNode *t_node = t_temp;
    139. // 移动到下一各节点
    140. t_temp = t_temp->next;
    141. // 释放保存内容的节点
    142. free(t_node);
    143. }
    144. }
    145. /**
    146. * 翻转链表
    147. */
    148. void listReverse(ListNode *node)
    149. {
    150. ListNode * head = NULL, *now = NULL, *temp = NULL;
    151. head = node->next;
    152. // head是来保存我们翻转以后链表中的头节点的
    153. now = head->next;
    154. // now用来保存我们当前待处理的节点
    155. head->next = NULL;
    156. // 一定要置为NULL,否则可能导致循环
    157. while(now)
    158. {
    159. temp = now->next; // 利用一个临时指针来保存下一个待处理的节点
    160. now->next = head; // 将当前节点插入到逆序节点的第一个节点之前,并更改head指向
    161. head = now;
    162. node->next = head; // 使链表头指针指向逆序后的第一个节点
    163. now = temp; // 更新链表到下一个待处理的节点
    164. }
    165. }
    166. /**
    167. * 求长度
    168. */
    169. int listLength(ListNode *node)
    170. {
    171. ListNode *t_temp;
    172. int t_length = 0;
    173. for(t_temp = node->next; t_temp != NULL; t_temp = t_temp->next)
    174. {
    175. t_length++;
    176. }
    177. return t_length;
    178. }
    179. /**
    180. * 冒泡排序
    181. */
    182. void listBubbleSort(ListNode *node)
    183. {
    184. int t_length = listLength(node);
    185. int i,j;
    186. ListNode *t_temp;
    187. for(i = 0; i < t_length; i++)
    188. {
    189. t_temp = node->next;
    190. for(j = 0;j < t_length - i - 1; j++)
    191. {
    192. if(t_temp->val > t_temp->next->val)
    193. {
    194. int t_data = t_temp->val;
    195. t_temp->val = t_temp->next->val;
    196. t_temp->next->val = t_data;
    197. }
    198. t_temp = t_temp->next;
    199. }
    200. }
    201. }
    202. /**
    203. * 定义快速排序算法
    204. */
    205. void quickSort(struct _ListNode *head, struct _ListNode *tail) {
    206. // 如果链表为空或只有一个节点,直接返回
    207. if (head == NULL || head == tail) return;
    208. // 定义两个指针p和q,用于分割链表
    209. struct _ListNode *p = head, *q = head->next;
    210. // 选取第一个节点作为基准值
    211. int pivot = head->val;
    212. // 遍历链表,将小于基准值的节点放到p的后面
    213. while (q != tail->next) {
    214. if (q->val < pivot) {
    215. p = p->next;
    216. // 交换p和q指向的节点的值
    217. int temp = p->val;
    218. p->val = q->val;
    219. q->val = temp;
    220. }
    221. q = q->next;
    222. }
    223. // 交换head和p指向的节点的值,使得p指向的节点为基准值
    224. int temp = head->val;
    225. head->val = p->val;
    226. p->val = temp;
    227. // 对左右两部分递归进行快速排序
    228. quickSort(head, p);
    229. quickSort(p->next, tail);
    230. }
    231. /**
    232. * 快速排序
    233. */
    234. void listQuickSort(ListNode *node)
    235. {
    236. ListNode *tail = node->next;
    237. while (tail->next)
    238. {
    239. tail = tail->next;
    240. }
    241. quickSort(node, tail);
    242. }
    243. /**
    244. * 堆排序
    245. */
    246. void listHeapSort(ListNode *node)
    247. {
    248. }
    249. /**
    250. * 获取链表倒数第k个节点,双指针方法
    251. */
    252. ListNode* listFindKthToTail(ListNode *node, int k)
    253. {
    254. // 超过长度直接返回空
    255. if(node == NULL || k >= listLength(node))return NULL;
    256. ListNode *first = node, *second = node;
    257. for(int i = 0; i < k; i++){
    258. first = first->next;
    259. }
    260. while (first)
    261. {
    262. first = first->next;
    263. second = second->next;
    264. }
    265. return second;
    266. }
    267. /**
    268. * 测试所有函数是否正确有效
    269. */
    270. int main(int argc, char* argv[])
    271. {
    272. //创建一个ListNode变量
    273. ListNode node;
    274. //创建链表
    275. listCreate(&node);
    276. int i = 0;
    277. for(i = 0;i < 10;i++)
    278. {
    279. #if 0
    280. listInsert(&node,i); // 插入数据头插法
    281. #else
    282. listTailInsert(&node, i); // 插入数据尾插法
    283. #endif
    284. }
    285. listDisplay(&node);
    286. ListNode* nodeFind = listFind(&node, 3);
    287. if(nodeFind)
    288. printf("listFind:%d\n", nodeFind->val);
    289. const int k = 5;
    290. ListNode* nodeFindK = listFindKthToTail(&node, k);
    291. if(nodeFindK)
    292. printf("listFindKthToTail step:%d :%d\n", k, nodeFindK->val);
    293. listModify(&node, 1, 999); //修改节点1999
    294. listDisplay(&node);
    295. listDelete(&node, 5); // 删除节点5
    296. listDisplay(&node);
    297. // listBubbleSort(&node); // 冒泡排序
    298. listQuickSort(&node); // quick sort
    299. listDisplay(&node); // 打印链表数据
    300. listReverse(&node); // 翻转链表
    301. listDisplay(&node); // 打印反转后的链表
    302. listDestroy(&node); // 销毁链表
    303. return 0;
    304. }
  • 相关阅读:
    学习STM32第十九天
    LeetCode(力扣)47.全排列 IIPython
    文献学习-14-一种用于高精度微创手术的纤维机器人
    Spring Cloud Gateway 搭建网关
    构建用于签名/加密双证书测试体系的可执行命令
    通过媒体查询来实现 WPF 响应式设计
    背包问题讨论
    使用CycleGAN训练自己的数据集
    DSPE-PEG-FSHB,FSHB-PEG-DSPE,磷脂-聚乙二醇-卵泡刺激素多肽FSHB
    Leetcode 86. Partition List (链表好题)
  • 原文地址:https://blog.csdn.net/CHNIM/article/details/132712711