百科问答小站 logo
百科问答小站 font logo



真心觉得C语言链表很抽象 难学 该如何学习? 第1页

  

user avatar   tongjingHHU 网友的相关建议: 
      

说说我上课时是怎么讲链表这部分的吧,主要思路是带着同学们step by step地从无到有实现链表,在逐步改进的过程中体会,而不是直接给大家一个最终完善的代码看。


首先,对于这几种操作,数组是有点麻烦的:

删除数组中的一个元素、在数组中插入一个元素、在数组末尾添加一个元素。


因此人们想能否有一个办法,处理上面的问题,实现:

–当需要添加一个元素时,会自动添加;


–当需要减少一个元素时,会自动放弃该元素原来占有的内存;


–可以较方便地插入新的元素。



下面,分步骤实现。


1. 定义链表单个节点的数据结构。

       #include <stdio.h>  struct node {  int data;  node *next; };  int main() {  struct node p;  p.data = 1;  return 0; }     


2. 尝试用指针的方式来实现,但下面代码会报错

       #include <stdio.h>  struct node {  int data;  node *next; };  int main() {  struct node *p;  (*p).data = 1;  return 0; }     


3. 改进下,使用指针前需要先分配存储空间

       #include <stdio.h> #include <stdlib.h>  struct node {  int data;  node *next; };  int main() {  struct node *p;  p=(node *)malloc(sizeof(node));  (*p).data = 1;  return 0; }     


4. 实现两个节点的串联,并可以单步跟踪,观察节点在内存中的链接关系。


       #include <stdio.h> #include <stdlib.h>  struct node {  int data;  node *next; };  int main() {  struct node *p1,*p2;  p1=(node *)malloc(sizeof(node));  (*p1).data = 1;   p2=(node *)malloc(sizeof(node));  (*p2).data = 2;   p1->next = p2;  return 0; }     


5. 增加链表最后一个节点的结束标志

       #include <stdio.h> #include <stdlib.h>  struct node {  int data;  node *next; };  int main() {  struct node *p1,*p2;  p1=(node *)malloc(sizeof(node));  (*p1).data = 1;   p2=(node *)malloc(sizeof(node));  (*p2).data = 2;   p1->next = p2;   p2->next = NULL;   return 0; }     


6.增加一个变量,记录头指针,实现类似这样的效果:


       #include <stdio.h> #include <stdlib.h>  struct node {  int data;  node *next; };  int main() {  struct node *head;   struct node *p1,*p2;  p1=(node *)malloc(sizeof(node));  (*p1).data = 1;   p2=(node *)malloc(sizeof(node));  (*p2).data = 2;   p1->next = p2;   p2->next = NULL;   head = p1;   return 0; }     


7. 尝试利用循环语句,实现多个节点的初始化。可以体会如何利用有限的变量实现更多节点的初始化操作。

       #include <stdio.h> #include <stdlib.h>  struct node {  int data;  node *next; };  int main() {  struct node *head,*p1,*p2;;  int i;  head = 0;   for (i=1;i<=5;i++)  {   p1=(node *)malloc(sizeof(node));   (*p1).data = i;   if(head == 0)   {    head = p1;     p2 = p1;   }   else   {    p2->next = p1;     p2 = p1;   }    }  p2->next = 0;   return 0; }     


原理图为:


8. 将上面初始化的链表依次输出

       #include <stdio.h> #include <stdlib.h>  struct node {  int data;  node *next; };  int main() {  struct node *head,*p1,*p2;;  int i;  head = 0;   // 初始化链表  for (i=1;i<=5;i++)  {   p1=(node *)malloc(sizeof(node));   (*p1).data = i;   if(head == 0)   {    head = p1;     p2 = p1;   }   else   {    p2->next = p1;     p2 = p1;   }    }  p2->next = 0;   // 输出链表数据  node *p;  p = head;  printf("链表上各结点的数据为:
");  while(p!=0)  {   printf("%d ",p->data);   p=p->next;  }  printf("
");   return 0; }      


9.尝试删除链表中的一个节点。实现过程中发现,需要先找到对应的节点。另外,具体实现时发现,需要记录当前节点的前一个节点。

       #include <stdio.h> #include <stdlib.h>  struct node {  int data;  node *next; };  int main() {  struct node *head,*p1,*p2,*p;  int i;  head = 0;   // 初始化链表  for (i=1;i<=5;i++)  {   p1=(node *)malloc(sizeof(node));   (*p1).data = i;   if(head == 0)   {    head = p1;     p2 = p1;   }   else   {    p2->next = p1;     p2 = p1;   }    }  p2->next = 0;   // 删除数据为2的链表节点  p1 = head;  while (p1->data!=2)  {   p2 = p1;   p1 = p1->next;  }  p2->next = p1->next;  delete p1;    // 输出链表数据  p = head;  printf("链表上各结点的数据为:
");  while(p!=0)  {   printf("%d ",p->data);   p=p->next;  }  printf("
");   return 0; }     

实现这一步骤,已经可以体会到链表相对于数组的优点了。

这个例子中仅考虑被删除的节点在链表中间,还有被删除的节点是第一个节点、最后一个节点,没有这个节点的特殊情况,需要考虑。


10. 还有插入节点、新增节点、链表排序等功能,利用上面的思路,大家可以自行逐步实现。最后,贴一下相对完整的链表代码。

       // // 实现链表的基本操作 // #include <stdio.h> #include <malloc.h>  struct node // 链表上结点的数据结构 {  int data;  node *next; };  struct node *Create(void)  // 产生一条无序链表 {  struct node *p1,*p2,*head;  // p2指向最后一个结点, p1指向插入结点,head指向头结点  int a;  head = 0;  printf("产生一条无序链表,请输入数据,以-1结束:
");  scanf("%d",&a);  while(a!=-1)   // 1,3,2,4,-1  {   p1 = (node *)malloc(sizeof(node));   p1->data=a;   if(head == 0)  // 插入链表的首部   {    head = p1; p2 = p1;   }   else  // 插入链表尾   {     p2->next = p1; p2 = p1;   }   scanf("%d",&a);  }  if(head)    p2->next = 0;  return (head); }  void Print(struct node *head)  // 输出链表上各结点的数据 {  struct node *p;  p = head;  printf("链表上各结点的数据为:
");  while(p!=0)  {   printf("%d  ",p->data);   p=p->next;  }  printf("
"); }  struct node *Delete_one_node(struct node *head,int num)  // 根据数据值删除链表中的一个结点 {  struct node *p1,*p2;  if(head == 0)  {    printf("链表为空,无结点可删除!
");   return 0;  }  if(head->data == num)  //删除首结点  {       p1 = head;       head = head->next;       free( (void *)p1 );   printf("删除了一个结点!
");  }  else  {               p1 = head;       p2 = head->next;  // p1指向比较节点的前一个结点   while(p2->data != num && p2->next != 0)  // 找到要删除的结点   {      p1 = p2;        p2 = p2->next;   }   if(p2->data == num)  // 删除找到的结点   {     p1->next = p2->next;     free( (void *)p2 );    printf("删除了一个结点!
");   }   else    printf("链表上没有找到要删除的结点!
");  }  return head; }  struct node *Insert(struct node *head,struct node *p)  // 将一个结点插入链表中 {  struct node *p1,*p2;  if(head == 0 )  // 空链表,插入链表首结点  {         head = p;     p->next = 0;   return head;  }  if(head->data >= p->data)  // 非空链表,插入到链表的首结点  {   p->next = head;      head = p;   return head;  }  p2 = p1 = head;  while(p2->next && p2->data <p->data)  // 找到要插入的位置  {   p1 = p2;     p2 = p2->next;  }  if(p2->data < p->data)  // 插入链表尾  {   p2->next = p;    p->next = 0;  }  else  // 插入在p1和p2所指向的结点之间  {   p->next = p2;    p1->next = p;  }  return head; }  struct node *Create_sort(void)  // 产生一条有序链表 {  struct node *p1,*head = NULL;  int a;  printf("产生一条有序链表,请输入数据,以-1结束:
");  scanf("%d",&a);  while(a!=-1)  {   p1 = (node *)malloc(sizeof(node));  // 产生一个新结点   p1->data=a;   head = Insert(head,p1);  // 将新结点插入链表中   scanf("%d",&a);  }  return head; }   void deletechain(struct node *head)  // 释放链表上各结点占用的内存空间 {  struct node *p1;  while(head)  {   p1 = head;   head = head->next;   free( (void *)p1 );  } }  ///////////////////////////////////////////////////////////////////////////// int main() {  struct node * head;  int num;  head = Create();                       // 产生一条无序链表   1,3,2,5,-1   Print(head);                           // 输出链表上的各结点值   printf("输入要删除结点上的整数:
");  scanf("%d",&num);            head = Delete_one_node(head,num);      // 删除链表上具有指定值的结点   2,1  Print(head);       struct node *p1 = (node *)malloc(sizeof(node));  p1->data=4;  head = Insert(head,p1);                // 将新结点插入链表中  Print(head);   deletechain(head);                     // 释放整个链表的结点空间   head = Create_sort();                  // 产生一条有序链表   1,3,2,5,-1  Print(head);  deletechain(head);                     // 释放链表上各结点占用的内存空间   return 0; }     


其他相对复杂的知识,包括网上找到的较复杂的源代码,大家都可以用这种方式来学习。先看代码,理解,然后自己尝试step by step地实现,逐渐转换为自己的知识。




  

相关话题

  C 语言自带函数返回值为指针类型的数组为什么不需要释放内存? 
  既然谭浩强的c语言教材那么烂,那么应该选择什么书作为c语言教材? 
  请问学 C 有必要死磕指针吗? 
  C 语言中不同类型指针的大小是否完全相同,为什么? 
  如何评价某985老师所说的「C语言至少学10年才能懂」? 
  假如用汇编语言重制某游戏或者软件能否降低 CPU 性能损耗? 
  DOS 界面是如何制作的? 
  如何向一个零基础的人,解释学习计算机编程的正确顺序和原因? 
  为什么程序代码被编译成机器码就不能跨平台运行? 
  Visual Studio 2019可以用来玩C语言吗? 

前一个讨论
被一个男孩亲吻到天亮是个什么样的感觉?
下一个讨论
微信公众平台插入清晰的数学公式?





© 2024-11-28 - tinynew.org. All Rights Reserved.
© 2024-11-28 - tinynew.org. 保留所有权利