经典面试问题

本文列举了一系列经典的面试问题,涉及排序、链表操作、递归与循环、二叉树、字符串处理等。如:O(n)时间复杂度内对员工年龄排序、旋转数组的最小元素查找、递归与循环解法的比较、链表节点删除、倒数第k个节点寻找、链表反转、数组奇偶数排序、二叉树镜像、顺时针打印矩阵、特定和的二叉树路径、二叉搜索树转双向链表、字符串排列、多数元素查找、连续子数组最大和及首个只出现一次的字符查找。这些问题展示了算法在解决实际问题中的应用。

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

1.对上万个员工的年龄进行排序,时间复杂度不超过O(n),空间复杂度为常数级别(特点:带排序的值有一个范围)

void sortAge(int age[],int length){
    const int oldestage = 99;
    int timeAge [oldestage + 1]={0};  //第i个位置存i岁的人的个数
    for(int i = 0;i<length ; ++i){
        if( age[i]<0  || age[i] >99)
        ++ timeage [ age[i]] ;
    }

    int index = 0;
    for(int i = 0 ;i<oldestage ; ++i){
        for(int j = 0 ;j<timeage[i];++j)
        {   age[index] = i; //连续timeage[i]个人都是i岁
            ++index;
        }   
    }

}

2.旋转数组
输入一个递增数组的旋转数组,输出旋转数组的最小元素。
旋转之后的数组实际上可以划分为两个排序的子数组(采用二分查找的思想),用两个指针分别指向第一个元素和最后一个元素,找到中间的元素,判断中间位置前后元素的之判断该位置实在前段数组还是后段数组,
还是两段数组的边界。

递归与循环

递归的缺陷:递归会导致栈溢出:调用层级太多的时候 ,就会超出栈的容量。
采用递归求菲薄纳妾前n项和

long long Fib(unsigned int n){
  if(n <= 0)
      return 0;
  if(n  == 1 )
      return 1;

return Fib(n-1)+Fib(n-2);

}

上述解法会重复计算大量的数。
采用自底向上的循环解法 从Fib(0) 和 Fib(1)开始计算

long long  Fib(int n ){
int res[2] = {0 ,1};
if(n < 2)
    return res[n];
long long Fib1  = 1;   // Fib(1)
long long Fib2  = 0;    //Fib(0)
long long FibN = 0;
for(unsigned int i = 0; i <= n ; ++i){  //开始计算Fib(2)直到Fib(n)
        FibN = Fib1 + Fib2;
        Fib1 = FibN;
        Fib2 =Fib1;


    }

}

3.给定链表头指针 和节点的指针,在O(1)时间内删除链表的结点

将该节点下一个节点的位置的值将该节点的值覆盖,将下一个节点删除。

4.链表中的 倒数第k个节点:输入一个节点,输出该链表的倒数第k个节点(利用快指针慢指针)
两个指针指向头节点,快指针先向前走k-1步,然后两个指针再一起移动,最后快指针到达链表末尾的时候慢指针指向倒数第k个节点。

5.链表反转:输入一个链表的头节点,输出反转后的链表的头节点。

这里写图片描述

为了反转这个单链表,我们先让头结点的next域指向结点2,再让结点1的next域指向结点3,最后将结点2的next域指向结点1,就完成了第一次交换,顺序就变成了Header-结点2-结点1-结点3-结点4-NULL,然后进行相同的交换将结点3移动到结点2的前面,然后再将结点4移动到结点3的前面就完成了反转,思路有了,就该写代码了:

LinkedList ReverseSinglyLinkedList(LinkedList list)
{
    LNode   *tmp = NULL;
    LNode   *p = NULL;

    if (list == NULL)
    {
        return NULL;
    }
    tmp = list->next;
    while (tmp->next != NULL)
    {
        p = tmp->next;
        tmp->next = p->next;
        p->next = list->next;
        list->next = p;
    }
    return list;
}

6.调整数组的顺序,使奇数位于数组的前半部分,偶数位于后半部分。
解法:维护两个指针,第一个指针指向数组的第一个元素,只向后移动,
第二个指针指向数组的最后一个元素,只向前移动,在两个指针相遇前,如果第一个指针内容是偶数并且第二个指针的内容是奇数,交换两个指针的内容。

void ReOrder(int *pData,unsigned int length){

    if(pData ==  NULL || length <1)
        return ;
    int  *pBegin = pData;
    int  *pEnd = pData + length -1;
    while (pBegin < pEnd ){
        while(pBegin < pEnd &&( *pBegin %2 == 1))
            ++pBegin;
        while(pBegin < pEnd &&( *pEnd %2 == 0))
            --pEnd;
        if(pBegin < pEnd){
            *pBegin ^= *pEnd;
            *pEnd ^= *pBegin;
            *pBegin ^= *pEnd;
        }

    }
}

7.完成一个函数,输入一个二叉树,输出它的镜像
解法:先前序遍历树的每一个节点,当该节点存在子节点的时候,交换子节点的值。

8.顺时针转圈打印矩阵

9.二叉树中和为某一值的路径
10.二叉搜索树和双向链表:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表,不能创建任何新节点,只能调整树中节点指针的指向。
解法:中序遍历的过程中,将前驱和后继连接起来

11.字符串的排列,输入一个字符串,打印出字符串中字符的所有排列。
把字符串看成两部分:第一部分是它的第一个字符,第二部分是其余的字符

首先求所有可能出现在第一个位置的字符(把第一个字符和后面所有字符交换)
接着对剩余的部分重复同样的操作

12.数组中出现次数超过一半的数

13.连续子数组的最大和;输入一个整型数组,数组里有正数和负数,求所有子数组的最大和,要求时间复杂度为O(n)
从第一个元素开始逐个向后扫描,计算到当前和,如果和<= 0 则前面的抛弃,在开始技术,并且不断根系当前和,知道扫描到最后一个元素。

14.在字符串中找出第一个只出现一次的字符
定义一个哈希表,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值