免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: emacsnw
打印 上一主题 下一主题

一个关于单向链表的面试题。 [复制链接]

论坛徽章:
0
71 [报告]
发表于 2007-04-28 10:05 |只看该作者
两个步进不一样的指针是个好办法。

但如果有好的内存分配策略的话,应该可以简化这个问题。

比如说,链表的每一项,从前到后,分配的内存地址都是有序的的话,
只需要把list->next指针和list本身比较一下看是否符合顺序就知道是否找到节点了。

不过这个内存分配方法不好办。

论坛徽章:
0
72 [报告]
发表于 2007-04-28 12:17 |只看该作者
首先用1步长2步长的方法判断是否有环存在O(n)。
如果有环,那就再从头开始,看每个节点的next是不是空。如果是空,就是我们要找的节点;不是空的话,就把它置空。O(n)
以楼主的例子,到8的时候,前面的节点的next都是空了,然后8指向3,发现3的next为空,就找到了最终结果。

这样的方法复杂度时间O(n),空间O(1)
但是破环了原有的链表
如果想不破坏
就需要一个辅助链表保存原始链表的信息,这样就需要O(n)的空间

所以最终结论是时间空间都是O(n)

论坛徽章:
0
73 [报告]
发表于 2007-04-28 12:38 |只看该作者
我想到一个新算法,不知道行不行得通,大家看一下,先是直接开一个空间,让这整个空间的地址是连续的,然后根据上面的连表一个个考过去,因为地址是连续的,所以当出现一个指向的结构体的地扯比自已小的时候,这时就出现了一个循环,也就是找到了楼主的循环点,可是这里有一个能点就是这个考的过程,我现在还没想出来.大家看看,能不能实现。

[ 本帖最后由 jist12321 于 2007-4-28 12:39 编辑 ]

论坛徽章:
0
74 [报告]
发表于 2007-04-28 12:56 |只看该作者
原帖由 jist12321 于 2007-4-28 12:38 发表
我想到一个新算法,不知道行不行得通,大家看一下,先是直接开一个空间,让这整个空间的地址是连续的,然后根据上面的连表一个个考过去,因为地址是连续的,所以当出现一个指向的结构体的地扯比自已小的时候,这时 ...


为什么地址小的时候就可以断定是循环呢,这种说法没有任何根据

论坛徽章:
0
75 [报告]
发表于 2007-04-28 13:08 |只看该作者
原帖由 deadlylight 于 2007-4-28 12:56 发表


为什么地址小的时候就可以断定是循环呢,这种说法没有任何根据


对不起,后面没看下去,刚刚转回去看了一下,行不通。不过,一步二步法是绝对可以的,而且我认认应该是时间复杂度是最小的了。

论坛徽章:
0
76 [报告]
发表于 2007-04-28 13:24 |只看该作者
原帖由 星尘细雨 于 2007-4-28 10:05 发表
两个步进不一样的指针是个好办法。

但如果有好的内存分配策略的话,应该可以简化这个问题。

比如说,链表的每一项,从前到后,分配的内存地址都是有序的的话,
只需要把list->next指针和list本身比较一 ...

我想的方法跟你差不多啊,可是这个内存分配有序的话就不存在链表一说,直接用数组了,像我说用考出整个链表更可笑,链表尾都不知道,怎么可能知道考哪里为止。做了一回傻瓜。

论坛徽章:
0
77 [报告]
发表于 2007-04-28 16:36 |只看该作者
各位给了很多方法,可惜没给代码,小弟也就没认真看了。
长期做系统程序,算法思维僵化了,想从现在开始锻炼锻炼,我就直接贴代码了。
算法很笨,先用不同步长前进找到圈,然后把圈中所有元素存到数组中(c99动态数组),最后再从链表头走一遍,第一个在数组中存在的元素就是圈的首节点。
不会算复杂度,估计是O(n^3)左右了,太烂了,开始练习算法。

  1. #include <stdio.h>
  2. #include <stdlib.h>

  3. typedef struct list
  4. {
  5.     int val;
  6.     struct list *next;
  7. } list_t;
复制代码

  1. //不用细看,用来产生带圈的链表。
  2. //len参数指定链表长度,loop_pos指定最后一个元素指向链表中的第几个元素。
  3. //loop_pos如果大于len,则链表尾指向链表头。
  4. list_t *pro_list(int len, int loop_pos)
  5. {
  6.     list_t *head = malloc(sizeof(list_t));
  7.     list_t *t = head, *n = head;
  8.     int i;
  9.     head->val = 0;

  10.     for ( i=1; i<len; i++ )
  11.     {   
  12.         list_t *m = malloc(sizeof(list_t));
  13.         m->val = i;
  14.         t->next = m;
  15.         t = m;
  16.     }   

  17.     if ( loop_pos <= len )
  18.         for ( i=0; i<loop_pos; i++ )
  19.             n = n->next;

  20.     t->next = n;
  21.    
  22.     return head;
  23. }

复制代码

  1. //链表打印函数,不用细看
  2. void print_list(list_t *head)
  3. {
  4.     list_t *t = head;
  5.     while (1)
  6.     {
  7.         printf("val:%d, addr:%p\n", t->val, t);
  8.         if ( NULL == t->next )
  9.             break;

  10.         t = t->next;
  11.     }
  12. }
复制代码

  1. //用来找圈的头节点,被find_loop调用
  2. //addr保存的是圈中所有节点的地址,head指向链表头,lo_len表示圈中有多少个节点
  3. list_t *find_loop_begin(list_t *addr[], list_t *head, int lo_len)
  4. {
  5.     int i;
  6.     list_t *t = head;

  7.     while (1)
  8.     {
  9.         for ( i=0; i<lo_len; i++ )
  10.             if ( addr[i] == t )
  11.                 return addr[i];

  12.         t = t->next;
  13.     }

  14.     // should not be here
  15.     return NULL;
  16. }


  17. list_t *find_loop(list_t *head)
  18. {
  19.     list_t *t1 = head;
  20.     list_t *t2 = head->next;

  21.     while (1)
  22.     {
  23.         if ( t1 == t2 )
  24.         {
  25.             int lo_len = 1;
  26.             int i = 1;
  27.             t1 = t1->next;
  28.            //先找出圈中有多少个节点
  29.             while ( t1 != t2 )
  30.             {
  31.                 lo_len ++;
  32.                 t1 = t1->next;
  33.             }
  34.            //把圈中的所有几点存入数组
  35.             list_t *addr[lo_len];
  36.             addr[0] = t2;
  37.             t1 = t2->next;
  38.             while ( t1 != t2 )
  39.             {
  40.                 addr[i++] = t1;
  41.                 t1 = t1->next;
  42.             }
  43.             //找出圈中的第一个节点
  44.             return find_loop_begin(addr, head, lo_len);
  45.         }
  46.         t1 = t1->next;
  47.         t2 = t2->next->next;
  48.     }

  49.     return t1;
  50. }
复制代码


  1. int main()
  2. {
  3.     list_t *head = pro_list(20, 11);
  4.     list_t *lo = find_loop(head);
  5.     printf("val:%d, addr:%p\n", lo->val, lo);
  6.     //print_list(head);

  7. }
复制代码

论坛徽章:
0
78 [报告]
发表于 2007-04-28 17:01 |只看该作者

回复 74楼 deadlylight 的帖子

怎么没有根据?

这个是有个前提条件的!只是满足这个前提条件比较困难而已。

论坛徽章:
0
79 [报告]
发表于 2007-04-28 20:07 |只看该作者
原帖由 deadlylight 于 2007-4-28 12:17 发表
首先用1步长2步长的方法判断是否有环存在O(n)。
如果有环,那就再从头开始,看每个节点的next是不是空。如果是空,就是我们要找的节点;不是空的话,就把它置空。O(n)
以楼主的例子,到8的时候,前面的节点的ne ...



我觉得这个算法是可以的,还有那个大步小步的,应该也是可以的,不过对于推出的公式,还不能确定是否正确,因为大小步不一定是一圈,而是大步m圈,小步n圈之后才相遇的,想到这个就晕了,就搞不清公式是否正确,不过应该大小步是能解这个问题的。
再想想。。。

论坛徽章:
0
80 [报告]
发表于 2007-04-28 20:52 |只看该作者
原帖由 emacsnw 于 2007-2-6 04:27 发表
想了一下,似乎下面的算法可以:O(1)空间,O(n)时间。
两个额外指针p1, p2,开始都指向头S。设需要找到的循环部分第一个结点的位置为A,S到A的距离为x (x可能为0),链表的循环部分长度为y (y > 0)。

算法 ...



这个算法应该是看懂了,画了个图分析了一下,似乎是这样的,如果还是有什么地方不对,请指出来。还是很有意思的一道题目。

链表问题.jpg (115.92 KB, 下载次数: 54)

链表循环问题

链表循环问题
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP