单链表--链表

大家好,本篇博客将讲述单链表的逆序,希望大家在看这篇博客的时候,手里拿张纸,手写一下过程!!!

 

出现情况:第一轮基础笔试或者是技术一面

难度系数:中

面试题目:实现一个单链表的反转。例如: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)至于头插法,如果时间复杂度和空间复杂度不要求,应该是个比较容易理解的过程。

所以各有千秋,大家主要是了解思想,代码很好写。

 

posted @ 2018-07-04 22:18  国孩  阅读(737)  评论(0编辑  收藏  举报