单链表--链表
大家好,本篇博客将讲述单链表的逆序,希望大家在看这篇博客的时候,手里拿张纸,手写一下过程!!!
出现情况:第一轮基础笔试或者是技术一面
难度系数:中
面试题目:实现一个单链表的反转。例如:10 9 8 7 6 5 4 3 2 1
反转之后应为:1 2 3 4 5 6 7 8 9 10
要求10分钟之内写出代码,注意代码风格以及时间复杂度。
考点分析
(1)基本概念:链表基础,代码规范问题,代码健壮性问题
(2)对时间复杂度敏感
解题思路:“一问,二画,三答”
其实对于这个问题,我们下面先给出解题思路的几种方式,来解释为什么要先问清楚(首先要问清楚面试官侧重的是什么)!!!
下面以角色的身份来阐述本问题的思路
1.小张
“我学过链表的基本操作,我可以重新申请一个链表,利用链表的前插法(头插法),这样就可以实现了链表的反转,我的思路是这样的。”
我重新从创建空表开始,一个一个将链表的结点插入到新开辟的链表的表头,也就是头结点之后,如下图看我的思路。
我还写好了算法了呢,看下面
LinkList ReverseList(LinkList &L){ //从表尾到表头逆向建立单链表L,每次均在头结点之后插入元素 LNode *s;int x; L=(LinkList)malloc(sizeof(LNode)); //创建头结点 L->next=NULL; //初始为空链表 scanf("%d", &x); //输入结点的值 while(x!=9999) { //输入 9999 表示结束 s=(LNode*)malloc(sizeof(LNode) ); //创建新结点 s->data-x; s->next=L->next; L->next=s; //将新结点插入表中,L为头指针 scanf ("%d", &x); } //while 结束 return L; }
2.小杨
“是的,你上面的代码可以实现要求,不过,我觉得空间复杂度为比较大,我有一个方法,借助三个指针来实现,下面是我的思路”
上面是我的思路,下面是我的核心代码,代码和上面思路完全一样
LinkList reverse_link(LinkList list){ if (NULL == list || NULL == list->next) return list; LinkList temp ,preV,next; preV = list; temp = list->next; preV->next = NULL; while (temp != NULL) { next = temp->next; temp->next = preV; preV = temp; temp = next; } return prev; }
这个代码的时间复杂度为O(n),另外仅仅申请了两个变量来存储额外信息。
3.小糊涂
“我和你们的思路都不一样,你看看所有的都是后移后移,
如果我们把当前的结点逆序,我们先可以这样,把它的后继结点都逆序了,然后再把逆序好的尾结点的next指针再指向当前结点就好了吗”
这样就需要比较少的代码啦,在这个里面要注意一点递归出口,在这里我是把链表为空或者链表只有一个结点当作递归的出口,下面是我的代码,代码比较简洁
list *reverse(list *p, list *&head) { if(p == NULL || p->next == NULL) { head = p; return p; } else { list *temp = reverse(p->next, head); temp->next = p; return p; }
}
4.小孙
上面利用递归操作(也是栈的应用),实际上可以很好的解决问题,我的思路也是利用栈操作,使用栈的先进后出特性,完成对链表的逆序输出。
下面是我的代码,思路大家看代码就会清楚
//逆向输出 stack<int> reOutput(LinkNode *linkNode) { LinkNode *head; stack<int> s; head = linkNode; while (head != NULL) { s.push(head->value); head = head->next; } return s; } stack<int> s; s = reOutput(linkNode); while (!s.empty()) { cout << s.top() << endl; s.pop(); }
通过上面先压栈,然后再出栈,自定义栈的大小,可以很好的解决问题,据听说,这是很多大公司采取的方案!!!
5.老师发言
当大家碰到这个题目的时候,首先要问明白这个出题人的思想,问清楚是否对链表做出限制
(1)如果说不能更改链表的结构,显然第二种方法就不行了,如果对链表的结构没有什么要求,这个方法还是很好的,毕竟时间复杂度为O(n),空间复杂度为常数S(1)
(2)如果数据比较繁多,虽然递归操作代码量比较少,但是可能会导致栈溢出(而且问题也比较难排查出),所以递归有这个风险,如果能克服这个风险,递归也是个不错的选择。
(3)假如有100w条数据,可能会导致栈溢出,我们可以利用栈操作,因为栈的特性是先进后出,所以就可以很好的解决了这个倒序问题,而且我们自定义栈,栈溢出,也被考虑到了。
(4)至于头插法,如果时间复杂度和空间复杂度不要求,应该是个比较容易理解的过程。
所以各有千秋,大家主要是了解思想,代码很好写。