从链表存储结构等算法问题中发现数据结构的重要性
首页 > 后端开发   作者: 凌波  2016-9-23 1:28:10  热度:6073°  字号:   评论:
时间:2016-9-23 1:28:10   热度:6073°  评论:  

这一弹,让我重新认识数据结构与算法的重要性,正是前面所说过,只有熟悉底层原理,才能很好的掌舵,让上层(灵活变通)写出高效的代码。也许很多时候开发起来,感觉不到这一点,那最重要还是自己练习少了,或者涉及这方面的算法几乎可以忽略,当然更多的还是活在“别人的开发中”。

好了,说正事,这次题说的数据结构中的链表,它是一种动态数据结构,在创建链表时无需指定链表的长度,插入数据时动态调整内存分配,因此没有闲置的内存,所以链表比数组空间效率高,但是链表无法保证内存存储数据是连续的,从链表中找数据,每次都要从头部节点开始循环找,所以相比数组的时间复杂度O(1)要大,所以它的时间复杂度与链表中的结点有关,因此链表的时间复杂度为O(n)!

问题描述:从尾到头打印链表

这题看上去很简单,实现方式各种各样,但是换个角度去思考这个问题,针对某一个点去解决,而不是为了强行去实现这个功能,否则就失去了题目本身的意义了!

在C#中List<T>泛型集合就是一个链表的存储结构,泛型集合也比较灵活,那就通过它为这题提供链表,要求反向打印链表中数据,这个特点正好符合栈(后进先出)的特性,那就可以用栈来实现这样的打印顺序了,遍历链表中数据一个个写入栈中,然后遍历栈一次打印输出,这种实现思路清晰简单,那就要看自己能不能把题目要求的特点分析出来,实现代码:

 /// <summary>
        /// 将list集合数据依次入栈
        /// </summary>
        /// <param name="list"></param>
        static void ListToStack(List<int> list)
        {
            Stack<int> s = null;
            if (list != null && list.Count > 0)
            {
                s = new Stack<int>();
                foreach (var item in list)
                {
                    s.Push(item);
                }
                foreach (var item in s)
                {
                    Console.WriteLine(item);
                }
            }
        }

对于“后进先出”这一特点,可以联想到递归,因为递归的本质也是一种栈结构,于是很自然的又想到用递归来实现此算法,那么可以在打印链表中结点的时候,可以先去递推打印它的下一结点,最后依次回归打印,就实现了反向打印链表,具体实现代码:

 /// <summary>
        /// 递归实现后进先出
        /// </summary>
        /// <param name="list"></param>
        /// <param name="i"></param>
        static void RevWriteList(List<int> list ,int i=0)
        {
            if(list!=null)
            {
                if(i<list.Count)
                {
                    if(++i<list.Count)
                    {
                        RevWriteList(list, i);
                    }
                    
                    Console.WriteLine(list[--i]);
                }
            }
        }


上面也说到重点不在于实现功能,其功能的实现都是相对于某个点来的,那么就上面两种解法,以时间效率这个点来说,到底谁强谁弱,不好直接妄下定论,那么就交给数据来定夺吧!

首先,往链表中提供6条数据,具体计算代码:

 List<int> list = new List<int>();
            list.Add(1);
            list.Add(2);
            list.Add(3);
            list.Add(4);
            list.Add(5);
            list.Add(6);
            sw.Start();
            ListToStack(list);
            sw.Stop();
            Console.WriteLine("\r\n直接用栈耗时:{0}", sw.Elapsed);
            sw.Restart();
            RevWriteList(list);
            sw.Stop();
            Console.WriteLine("\r\n用递归实现栈的特点耗时:{0}", sw.Elapsed);

代码中分别计算了直接通过栈实现和用递归来实现所需要的时间。具体运行结果:

QQ截图20160923004248.png

       很明显的看到两者的差别,用递归来实现远远甩直接用栈好几条街,这是为什么呢,自己简单的分析了下原因,用栈实现时,需要 new Stack<int>()开辟空间,然后把数据取出来一个个入栈,我想就是这里相对耗时要多点,而对于递归,并没有开辟内存空间这些操作,所以这里递归乐胜一筹!

接下来,尝试把链表中数据添加足够多些(比如再加100条数据),在来执行如上代码,结果显示:


QQ截图20160923010947.png

很清楚的发现,递归耗时慢慢会超越直接用栈的耗时。怎么一下子剧情又扭转了呢,这就是递归算法本身的原因了,虽然递归来实现,代码干净整洁(牛逼),但它也是有瓶颈的,如果链表的结点过多之后,会导致函数调用层级过深,从而可能导致函数调用栈溢出!所以具体是否用递归是环境所定,不过一般不提倡用递归算法设计程序,因为在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储!

总结,任何问题的存在,都是具有意义效应的,否则都是浮云!

 您阅读这篇文章共花了: 
捐赠支持:如果觉得这篇文章对您有帮助,请“扫一扫”鼓励作者!
二维码加载中...
本文作者: 凌波      文章标题: 从链表存储结构等算法问题中发现数据结构的重要性
本文地址:http://www.lingbohome.com/Article/Post/30
版权声明:若无注明,本文皆为“凌波小屋”原创,转载请保留文章出处。

发表吐槽

返回顶部    首页    大事记   动态    捐赠支持    后花园   
版权所有:Copyright ©2016 凌波小屋 All rights All rights reserved    站长: 凌波       程序:.Net Mvc   鄂ICP备 15003636号-1