剑指offer学习之字符串和链表

本文探讨了编程中字符串和链表的高效处理方法,包括字符串替换、链表逆序打印及数据结构特性。强调了时间复杂度优化和内存管理的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2.3.2字符串

      字符串是由若干字符组成的序列,由于字符串在编程里面使用的效率非常高,为了优化,很多编程语言都对字符串做了特殊的规定。

c\c++中每个字符串都以'\0'结尾,虽然利用此我们能很方便的找到字符串的尾部,但这样每个字符串都有一个额外字符的开销,而且很容易造成字符串越界。

比如

char str[10];

strcpy(str, '0123456789');

     这样写就不对,str应该写成11个字符。

    为了节省内存,c/c++把常量字符串放到单独的一个内存区域,当几个指针赋值相同的常量字符串时,它们实际指向的是同一个地址。用常量内存初始化数组,则有不同。如果是给两个数组初始化相同的常量字符串,可这两个数组并不相同。

面试题5:替换空格

题目:请实现一个函数,把字符串中的每个空格替换为"%20",例如"we are happy."替换为"we%20are%20happy."

    在网络编程中,如果URL参数中函数中含有特殊字符,比如空格,#,则导致服务器无法正确识别,这时候需要把这些特殊字符转换为服务器可以识别的字符。转换的规则是在'%'后跟上ASCII码。空格ASCII码是32,用十六进制表示是'0x20'.'#'的ASCII码是35,十六进制表示是0x23.

     这道题有两种需求,一种需求是在原来的字符串进行替换,则有可能覆盖修改在字符串后面的内存。一种是使用新的字符串进行替换,则我们要分配好足够的内存。假设是在原来的字符串上进行替换,并且保证输入的字符串后面有足够多的空余内存。

注意:时间复杂度为O(n*n)的解法,不足以拿到offer.

    假如我们从头到尾扫描字符,遇到空格就替换,如果有n个空格,每替换一次,空格后面的所有字符都要移动一次,这样的时间效率为O(n*n).

思路:先遍历,算出替换之后字符串的长度,使用两个指针p1和p2,p1指向原字符串末尾,p2指向替换后字符串末尾,依次从后向前遍历,这样的时间复杂度为O(n).

测试用例:(1)输入的字符串中包含空格(空格位于字符串最前面;空格位于字符串最后面;空格位于字符串中间;字符串中有连续多个空格);

                  (2)输入的字符串中没有空格;

                  (3)特殊输入测试(字符串为nullptr;字符串是一个空字符串;字符串中只有一个空格字符;字符串中有连续多个空格)。

本题考点:(1)对字符串的编程能力

                  (2)考查应聘者分析时间效率的能力

                  (3)考查应聘者对内存覆盖能力是否有足够的警惕,在分析得知字符串会变长之后,是否能够意识到潜在的问题,并及时与面试官沟通

                   (4)考查应聘者的思维能力

学习:++ i;

相关题目:有两个排序的数组A1和A2,内存在A1的末尾有足够多的空余空间容纳A2,请实现一个函数,把A2中的所有数字插入A1中,并且所有的数字是排序的。

思路:从尾到头比较A1和A2中的数字,并且把较大的数字拷贝到A相应的位置。

 

2.3.3链表

    链表应该是面试中被提及最频繁的数据结构。链表的结构很简单,它是由指针把若干个节点连接成链状结构。链表的创建、插入节点、删除节点等操作只需要20行左右代码就能实现,其代码量比较适合面试。而像哈希表、有向图等复杂数据结构,实现它们的一个操作需要的代码数据量都很大,很难在几十分钟内的面试内完成。另外,由于链表是一种动态的数据结构,需要对指针进行操作,因此操作者需要很好的编程功底才能写出完整的操作链表的代码。而且链表这种数据结构很灵活,面试官可以用链表设计出具有挑战性的面试题。

      我们说链表是一种动态数据结构,是因为在创建链表时,无须知道链表的长度。当插入一个新节点时,我们只需要为新节点分配内存,然后来调整指针的指向来确保新节点被链接到链表当中。内存分配不是在创建链表时一次性完成的,而是每添加一个节点分配一次内存。由于没有闲置的内存,链表的空间效率比数组高,如果单向链表的节点定义如下:

struct ListNode

{

    int m_nValue;

    ListNode* m_pNext;

}

      当我们往一个空链表中插入一个节点时,新插入的节点就是链表的头指针,由于此时会改动头指针,所以必须把pHead设为指向指针的指针,否则出了这个函数pHead依然是一个空指针。

      由于链表中的内存不是一次性分配的,因此我们无法保证链表的内存和数组一样是连续的,因此如果想要在链表中找到它的第i个节点,那么我们只能从头节点开始,沿着指向下一个节点的指针遍历链表,它的时间效率为O(n)。二者数组中我们可以根据下标在O(1)时间内找到第i个元素。下面是在链表中找到第一个含有某值的节点并删除该节点的代码

      除了简单的单向链表经常被设置为面试题(详见面试题6“从尾到头打印链表”、面试题18“删除链表的节点”、面试题22“链表中倒数第k个节点”、面试题24“反转链表”、面试题25“合并两个排序的链表”、面试题52“两个链表中的第一个公共节点”等,链表的其他形式也备受面试官青睐。

     (1)把链表末尾节点的指针指向头节点,从而形成一个环形链表(详见面试题62“圆圈中最后剩下的数字”);

     (2)链表中的节点除了有指向下一个节点的指针,还有指向前一个节点的指针,这就是双向链表(详见面试题36”二叉搜索树与双向链表“);

      (3)链表中的节点除了有指向下一个节点的指针,还有指向任意节点的指针,这就是复杂链表(详见面试题35”复杂链表的复制)。

面试题6:从尾到头打印链表

       题目:输入一个链表的头节点,从尾到头打印出每个节点的值。链表节点定义如下:

struct ListNode

{

     int m_nKey;

     ListNode* m_pNext;

}

思路:如果把链表中链表节点的指针反转过来,改变链表的方向,就可以从尾到头的打印,但这会修改链表的结构。

面试小提示:在面试中,如果我们打算修改输入的数据,则最好先问问面试官是否可以修改。

       通常打印是一个只读操作,我们不希望打印时修改内容,假设要求不能修改链表数据结构。那么解决这个问题肯定要遍历链表,遍历的顺序是从头到尾,而输出的顺序则是从尾到头。也就是说第一个遍历到的节点最后一个输出,而最后一个遍历到的节点第一个输出。这就是典型的“后进先出”,我们可以用栈实现这种顺序。每经过一个节点时,把该节点放到一个栈中。当遍历完整个链表后,再从栈顶部开始逐个输出节点的值,此时输出节点的顺序就反转过来了。

      可以用递归实现,每访问到一个节点时,先输出它后面的节点,再输出它自身。递归存在一个问题,当链表非常长的时候,会导致函数调用的层级很深,从而有可能导致函数调用栈溢出。

测试用例:(1)功能测试(输入的链表有多个节点,输入的链表只有一个节点);

                  (2)特殊测试(输入的链表头节点指针为nullptr).

本题考点:(1)考查应聘者对单向链表的理解和编程能力;

                  (2)考查应聘者对循环、递归和栈3个相互关联概念的理解。

cstdio是将stdio.h的内容用C++头文件的形式表示出来。stdio.h是C标准函数库中的头文件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值