单链表操作大全(图解逆序)

9 篇文章 0 订阅

 如果说你经常在linux中,或者在kernel下面做事的话,一定会碰到链表的操作。
如果你没有真正了解单链表,还是把基础打好吧。
如下程序综合了链表的常用方面,请你下自己写出每个函数,debug并运行,直到运行正确;然后对照参考程序,
比较程序的差异,有时候,可能你测试不全面,会有这样那样的错误,多思考,这样,你才记忆深刻。

[cpp]  view plain copy
  1. #include <stdio.h>    
  2. #include <stdlib.h>    
  3.     
  4. typedef struct node    
  5. {    
  6.     int nDate;    
  7.     struct node *pstnext;    
  8. }Node;    
  9. //链表输出    
  10. void output(Node *head)    
  11. {    
  12.     Node *p = head->pstnext;    
  13.     while(NULL != p)    
  14.     {    
  15.     printf("%d  ", p->nDate);     
  16.     p = p->pstnext;    
  17.     }    
  18.     printf("\r\n");    
  19. }    
  20. //链表建立    
  21. Node* creat()    
  22. {    
  23.     Node *head = NULL, *p = NULL, *s = NULL;    
  24.     int Date = 0, cycle = 1;    
  25.     head = (Node*)malloc(sizeof(Node));    
  26.     if(NULL == head)    
  27.     {    
  28.       printf("分配内存失败\r\n");    
  29.       return NULL;    
  30.     }    
  31.     head->pstnext = NULL;    
  32.     
  33.     p = head;    
  34.     while(cycle)    
  35.     {    
  36.         printf("请输入数据且当输入数据为0时结束输入\r\n");    
  37.         scanf("%d", &Date);    
  38.         if(0 != Date)    
  39.         {    
  40.             s = (Node*)malloc(sizeof(Node));    
  41.             if(NULL == s)    
  42.             {    
  43.                     printf("分配内存失败\r\n");    
  44.                     return NULL;    
  45.             }    
  46.             s->nDate = Date;    
  47.             p->pstnext = s;    
  48.             p = s;    
  49.         }    
  50.         else    
  51.         {    
  52.             cycle = 0;    
  53.         }    
  54.     }    
  55.     p->pstnext = NULL;    
  56.     return(head);    
  57. }    
  58. //单链表测长    
  59. void length(Node *head)    
  60. {    
  61.     Node *p = head->pstnext;    
  62.     int j=0;    
  63.     while(NULL != p)    
  64.     {    
  65.         p = p->pstnext;    
  66.         j++;    
  67.     }    
  68.     printf("%d\r\n", j);    
  69. }    
  70. //链表按值查找    
  71. void research_Date(Node *head, int date)    
  72. {    
  73.     Node *p;    
  74.     int n=1;    
  75.     p = head->pstnext;    
  76.     while(NULL != p && date != p->nDate)    
  77.     {       
  78.          p = p->pstnext;    
  79.         ++n;    
  80.     }    
  81.     if(NULL == p)    
  82.     {    
  83.           printf("链表中没有找到该值");    
  84.     }  
  85.     else if(date == p->nDate)    
  86.     {    
  87.         printf("要查找的值%d在链表中第%d个位置\r\n", date, n);    
  88.     }    
  89.     return;    
  90. }    
  91. //按序号查找    
  92. void research_Number(Node *head, int Num)    
  93. {    
  94.     Node *p=head;    
  95.     int i = 0;    
  96.     while(NULL != p && i < Num)    
  97.     {    
  98.         p = p->pstnext;    
  99.         i++;    
  100.     }    
  101.     if(p == NULL)    
  102.     {    
  103.         printf("查找位置不合法\r\n");    
  104.     }  
  105.     else if(i == 0)    
  106.     {    
  107.         printf("查找位置为头结点\r\n");    
  108.     }  
  109.     else if(i == Num)    
  110.     {    
  111.         printf("第%d个位置数据为%d\r\n", i, p->nDate);    
  112.     }    
  113. }    
  114. //在指定元素之前插入新结点    
  115. void insert_1(Node *head, int i, int Newdate)    
  116. {    
  117.     Node *pre = head, *New = NULL;    
  118.     int j = 0;    
  119.     while(NULL != pre && j < i-1)    
  120.     {     
  121.         pre = pre->pstnext;    
  122.         j++;    
  123.     }    
  124.     if(NULL == pre || j > i-1)    
  125.     {    
  126.         printf("插入位置不存在\r\n");    
  127.     }  
  128.     else    
  129.     {    
  130.         New = (Node*)malloc(sizeof(Node));    
  131.         if(NULL == New)    
  132.         {    
  133.             printf("分配内存失败\r\n");    
  134.             return;    
  135.         }    
  136.         New->nDate = Newdate;    
  137.         New->pstnext = pre->pstnext;    
  138.         pre->pstnext = New;    
  139.     }    
  140.     
  141. }    
  142. //在指定元素之后插入新结点    
  143. void insert_2(Node *head, int i, int Newdate)    
  144. {    
  145.     Node *pre = head, *New = NULL;    
  146.     int j = 0;    
  147.     while(NULL != pre->pstnext && j < i)    
  148.     {    
  149.         pre = pre->pstnext;    
  150.         j++;    
  151.     }    
  152.     if( j == i)    
  153.     {    
  154.         New = (Node*)malloc(sizeof(Node));    
  155.         if(NULL == New)    
  156.         {    
  157.             printf("分配内存失败\r\n");    
  158.             return;    
  159.         }    
  160.         New->nDate = Newdate;    
  161.         New->pstnext = pre->pstnext;    
  162.         pre->pstnext = New;    
  163.     }  
  164.     else    
  165.     {    
  166.         printf("插入位置不存在\r\n");    
  167.     }    
  168. }    
  169. //删除指定结点    
  170. void Delete_1(Node *head, int i3)    
  171. {    
  172.     Node *p = head, *pre = NULL;    
  173.     int j = 0;    
  174.     while(NULL != p && j < i3)    
  175.     {    
  176.         pre = p;    
  177.         p = p->pstnext;    
  178.         j++;    
  179.     }    
  180.     if(NULL == p)    
  181.     {    
  182.         printf("删除位置不存在\r\n");    
  183.     }  
  184.     else    
  185.     {    
  186.         pre->pstnext = p->pstnext;    
  187.         free(p);    
  188.     }    
  189. }    
  190. //指定删除单链表中某个数据,并统计删除此数据的个数    
  191. int Delete_2(Node *head, int Delete_date)    
  192. {    
  193.     int count = 0;    
  194.     Node *p = head, *q;    
  195.     while(NULL != p->pstnext)    
  196.     {       
  197.         q = p->pstnext;    
  198.         if(q->nDate == Delete_date)    
  199.         {    
  200.             p->pstnext = q->pstnext;    
  201.             free(q);    
  202.             ++count;    
  203.         }    
  204.         else    
  205.         {    
  206.             p = q;    
  207.         }    
  208. }    
  209. return count;    
  210. }    
  211. //链表逆置,一定要掌握好,详解见图   
  212. void Reverse_list(Node *head)    
  213. {    
  214.     Node *q, *s;    
  215.     if(NULL == head->pstnext || NULL == head->pstnext->pstnext)  //是否要判断head为空?  
  216.     {    
  217.         return;    
  218.     }    
  219.     q = head->pstnext->pstnext;    
  220.     head->pstnext->pstnext = NULL;    
  221.     while(NULL != q)    
  222.     {    
  223.         s = q->pstnext;    
  224.         q->pstnext = head->pstnext;    
  225.         head->pstnext = q;    
  226.         q = s;    
  227.     }    
  228. }    
  229. //单链表的连接    
  230. void connect_list(Node *head, Node *head_New)    
  231. {    
  232.     Node *p = head;    
  233.     while(NULL != p->pstnext)    
  234.     {       
  235.         p = p->pstnext;    
  236.     }    
  237.     p->pstnext = head_New->pstnext;    
  238. }    
  239. //单链表销毁    
  240. void destroy_list(Node* head)    
  241. {    
  242.     while (NULL != head)    
  243.     {    
  244.         Node* temp = head;    
  245.         head = head->pstnext;    
  246.         free(temp);    
  247.     }    
  248. }    
  249. main()    
  250. {    
  251. int date, num;    //待查找数据    
  252. int i3;     //指定删除元素的位置    
  253. int i1, i2, Newdate_1, Newdate_2;    //待插入的新数据    
  254. int Delete_date, k;    //待删除的数据与其个数    
  255. Node *Head = NULL;   //定义头结点    
  256. Node *Head_New = NULL;    
  257.     
  258. //链表建立    
  259. Head = creat();    
  260. printf("输出建立的单链表\r\n");    
  261. output(Head);    
  262.     
  263. //单链表测长    
  264. printf("单链表长度为\r\n");    
  265. length(Head);    
  266.     
  267. //链表按值查找    
  268. printf("请输入待查找的数据\r\n");    
  269. scanf("%d", &date);    
  270.     research_Date(Head, date);    
  271.     
  272. //链表按序号查找    
  273. printf("请输入待查找序号\r\n");    
  274. scanf("%d", &num);    
  275. research_Number(Head, num);    
  276.     
  277. //在指定第i1个元素之前插入新元素Newdate    
  278. printf("在指定第i个元素之前插入新元素Newdate");    
  279. printf("请输入i与元素且以逗号间隔\r\n");    
  280. scanf("%d,%d", &i1, &Newdate_1);    
  281. insert_1(Head, i1, Newdate_1);    
  282. printf("插入后新链表\r\n");    
  283. output(Head);     
  284.     
  285. //在指定第i2个元素之后插入新元素Newdate    
  286. printf("在指定第i个元素之后插入新元素Newdate");    
  287. printf("请输入i与元素且以逗号间隔\r\n");    
  288. scanf("%d,%d", &i2, &Newdate_2);    
  289. insert_2(Head, i2, Newdate_2);    
  290. printf("插入后新链表\r\n");    
  291. output(Head);     
  292.     
  293. //指定删除i3元素    
  294. printf("删除元素的位置\r\n");    
  295. scanf("%d", &i3);    
  296. Delete_1(Head, i3);    
  297. printf("删除后新链表\r\n");    
  298. output(Head);    
  299.     
  300. //指定删除单链表中某个数据,并统计删除此数据的个数    
  301. printf("请输入待删除的元素\r\n");    
  302. scanf("%d", &Delete_date);    
  303. k = Delete_2(Head, Delete_date);    
  304. printf("删除后新链表\r\n");    
  305. output(Head);    
  306. printf("删除指定元素在链表中的个数为:");    
  307. printf("%d\r\n", k);    
  308.     
  309. //单链表逆置    
  310. Reverse_list(Head);    
  311. printf("逆置后输出\r\n");    
  312. output(Head);    
  313.     
  314. //单链表的连接    
  315. printf("建立一个新链表\r\n");    
  316. Head_New = creat();    
  317. printf("输出新链表");    
  318. output(Head);    
  319. printf("将新链表连接到原来链表的尾部并输出\r\n");    
  320. connect_list(Head, Head_New);    
  321. output(Head);    
  322.          destroy_list(Head);    
  323.     
  324. }   


如果,你对前面几个操作比较熟悉的话,单链表逆序就能考验你的能力了,(其实也不难),关键是思路清晰。
首先,我们看下单链表逆序的原理,如果你理解错了,可能程序也写不出来。
原理如图所示:

 

这里我们分析一下程序中的逆序函数,特别提出的是,这里的单链表是含有header的单链表。
为画图方便,这里,我们暂时只假定链表中只有3项,1,2和3.更多的情况类同。
每条语句的含义都在图中详细解释了。

 

附1:无链表头的单链表逆序程序

[cpp]  view plain copy
  1. typedef struct student  
  2. {  
  3.     int number;  
  4.     char name[20];  
  5.     int score;  
  6.     struct student *next;  
  7. }student;  
  8.   
  9.   
  10. student *reverse2(student *stu)  
  11. {  
  12.         student *p1,*p2,*p3;  
  13.         if(stu == NULL ||stu->next == NULL)  
  14.                 return stu;  
  15.         p1=stu;                                   //p1指向链表的第一个节点                                                    
  16.           p2=p1->next;  
  17.         p1->next = NULL;  
  18.         while(p2)  
  19.         {  
  20.                 p3=p2->next;  
  21.                 p2->next = p1;  
  22.                 p1=p2;  
  23.                 p2=p3;  
  24.         }  
  25.         printf("p1 = %d,next = %d\n ",p1->number,p1->next->number);  
  26.         stu=p1;                                  //将链表第一个节点指向p1  
  27.         return stu;  
  28. }  


 

附2:从无头单链表中删除节点
题目:

        假设有一个没有头指针的单链表。一个指针指向此单链表中间的一个节点(非第一个节点, 也非最后一个节点)。请将该节点从单链表中删除。

 

解答:

        典型的“狸猫换太子”, 若要删除该节点,正常情况下,应该要知道该节点的前面节点的指针,但是由于单链表中没有头结点,所以无法追溯到该节点前面的那个节点,因此,这里采用了“移花接木”的方法。设该节点为B,下一个节点为C。那么,首先将B节点的内容替换为C节点的内容,然后,将C节点删除,这样就达到了我们的目的。代码如下:

[cpp]  view plain copy
  1. pcur->next = pnext->next;  
  2.   
  3. pcur->data = pnext->date;  
  4.   
  5. delete pnext;  

代码

[cpp]  view plain copy
  1. void DeleteListNode(node* pCurrent)    
  2. {        
  3.  assert(pCurrent != NULL);        
  4.  node* pNext = pCurrent -> next;       
  5.  if (pNext == NULL)        
  6.   pCurrent = NULL;      
  7.  else    
  8.  {       
  9.   pCurrent -> next = pNext -> next;    
  10.   pCurrent -> data = pNext -> data;     
  11.   delete pNext;       
  12.  }  
  13. }  

类似问题:

1、 从无头单链表中删除节点问题:假设有一个没有头指针的单链表,一个指针p指向单链表中的一个节点(不是第一个,也不是最后一个),请将该节点删除掉。

2、 向无头单链表中添加节点问题:假设有一个没有头指针的单链表,一个指针p指向单链表中的一个节点(不是第一个,也不是最后一个),请在该节点之前插入一个新的节点q。

 

        由于链表是无头单向链表,所以我们无法由当前节点获得p的前一节点,而无论是删除当前节点还是向前面节点插入新节点都需要获得p的前一节点。在这里我们不妨换一下思路,对当前节点的后继结点进行操作,然后将前后节点的数据进行适当交换也可以得到相应效果。

问题1解法:将p后继结点p->next的数据拷贝到p,然后删除p->next,这样就达到了相同的效果,代码如下:

[cpp]  view plain copy
  1. ListNode* p_next = p->next;  
  2.   
  3.                        p->value=p_next->value;  
  4.   
  5.                        p->next=p_next->next;  
  6.   
  7.                        delete   p_next;  


问题2解法:在p节点后添加q,然后交换p和q的数据即可。

[cpp]  view plain copy
  1. q->next=p->next;  
  2.   
  3.                        p->next=q;  
  4.   
  5.                        swap(&p->value, &q->value);  


 

扩展问题:

        将一个单链表,在只遍历一遍的情况下,将单链表中的元素顺序反转过来。

解答:

        我的想法是这样的,用三个指针进行遍历,在遍历的途中,进行逆置。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值