《剑指offer》系列---2

2023-11-20

1.求斐波那契数列的第N项

这个题目很简单,讲递归的书上都是用这个来讲的,但是面试的时候,如果你写个递归,那估计会让人失望的,因为递归的效率真是一个问题,你可以测试一下,输入50,基本上得到结果的时间,够你去喝杯茶了

#include <iostream>
using namespace std;

//使用递归效率太低了,甚至可能造成栈溢出
/*int fabonacci1(int n)
{
	if(n <= 0)
		return 0;
	else if(n == 1)
		return 1;
	else return fabonacci1(n-1)+fabonacci1(n-2);
}*/

int fabonacci2(int n)
{
	int num1 = 0, num2 = 1, num3 = 0;
	int i = 2;
	while(i <= n)
	{
		num3 = num1+num2;
		num1 = num2;
		num2 = num3;
		i++;
	}
	return num3;
}

int main()
{
	int ret2 = fabonacci2(5);
	int ret1 = fabonacci1(5);
	cout<<ret2<<endl;
	cout<<ret1<<endl;
	return 0;
}

剑指offer上还给我们列出了一个数学公式,我觉得就没必要掌握了,但是搞算法的同学还是应该去了解一下

2.输入一个整数,输出它的二进制表示中1的个数

这个题目考察的是我们对位运算的运用,作为一道面试题,自然,我们应当记住最好的解法

第一种:直接和1做&运算,如果最低位是1则增加1,然后整个数右移一位,再与1做与运算,这样就是下面的

/*int number_of1_in_binary(int m)
{
    int count = 0;
    while(m)
    {
        if(m&1)
            count++;
        m>>=1;
    }
    return count;
}*/
但是这样有一个缺点,如果输入的数是负数怎么办呢?右移一位,左边的最高位补上的是1,到最后变成了0XFFFFFFFF,陷入了死循环

第二种:我们可以把移动1,每次比较之后,左移1一位,然后做与运算,就是下面的这样

/*int number_of1_in_binary(int m)
{
    int count = 0, flag = 1;
    while(flag)//每次都把flag往左移动移位,移动32次
    {
        if(m&flag)
            count++;
        flag = flag<<1;
    }
    return count;
}*/
但是这样我们就移动了32次,是不是有点多?,还有没有其它方法?

第三种方法:考察一个数7,二进制111,7-1 = 6的二进制是110, 然后&7 = 110是6, 6-1 = 5的二进制是101,然后&6 = 100是4, 4-1 = 3的二进制是011,然后&4 = 0,我们发现每次把这个数-1然后与上这个数,得到数是原来这个数的最后右边的1变为0的值,由此我们得到如下的解法:

int number_of1_in_binary(int m)
{
    int count = 0;
    while(m)
    {
        ++count;
        m = (m-1)&m;
    }
    return count;
}

3.实现库函数Power(double base, double exponent),不使用库函数,同时不考虑大数问题

如果你大笔一挥,一分种内写下如下的代码:那你就是和我一样的人,别人offer跳板的人

double Power(double base, int exp)
{
    double ret = 1.0;
    while(exp)
    {   
        ret *= base;
        exp--;
    }   
    return ret;
}

1.如果指数是负数怎么办?

2.如果指数是负数而且底数是0怎么办?0的倒数是没有意义的

再加上考虑的这两点,我们完善一下我们的代码

bool flag = true;

bool equal(double m, double n)
{
	if(m-n > -0.000001 && m-n < 0.000001)
		return true;
	else
		return false;
}
double power(double base, double exp)
{
	double ret = 1.0;
	while(exp)
	{
		ret *= base;
		exp--;
	}
	return ret;
}

double Power(double base, int exp)
{
	if(equal(base, 0.0) && exp < 0)
	{
		flag  = false;
		return 0.0;
	}
	unsigned int temp = (unsigned int)exp;
	if(exp < 0)
		temp = 0-exp;
	double ret = power(base, temp);
	if(exp<0) return 1.0/ret;
	return ret;
}

这里还考察了这些细微的编程知识,对于浮点数如何和0比较,对于错误如何返回更好

4. 输入一个数字,打印从1到最大的n位十进制数,比如输入3,打印从1到999的所有数字

如果我们反应快,很快就能想到,可以先求出这个最大的数,然后打印:

void print_to_max_number(int n)
{
    int m = 1;
    while(n)
    {   
        m *= 10; 
        n--;
    }   
    for(int i = 1; i < m; i++)
        cout<<i<<endl;
}
但是这里我们没有考虑大数的问题了,如果n很大怎么办?

大数问题一般都是用数组或者字符串存储每一位,这里我们用字符串

bool Increment(char *num)
{
	bool isOverFlow = false;
	int nTakeOver = 0;
	int len = strlen(num);
	int Num;

	for(int i = len-1; i >= 0; i--)
	{
		Num = num[i]-'0'+nTakeOver;
		if(i == len-1)
			Num++;
		if(Num >= 10)
		{
			if(i == 0)
				isOverFlow = true;
			else
			{
				Num -= 10;
				nTakeOver = 1;
				num[i] = Num + '0';
			}
		}
		else
		{
			num[i] = Num + '0';
			break;
		}
	}
	return isOverFlow;
}

void printnum(char *s)
{
	char *p = s;
	while(*p == '0') p++;
	cout<<p<<endl;
}

void print_to_max_number(int n)
{
	if(n <= 0)
		return;
	char *num = new char[n+1];
	memset(num, '0', n);
	num[n] = '\0';
	 
	while(!Increment(num))//对一个字符串自增,考虑进位
		printnum(num);//打印数字字符串注意的地方
	delete[] num;
}


5.给定单链表的头指针和一个节点指针,定义一个函数在O(1)复杂度内删除该节点

通常我们的思路就是遍历然后查找,时间复杂度是O(n),找到这个节点的前一个节点,然后把前一个节点的next指向这个节点的下一个节点

现在我们已经给定了这个节点了,我们换一种思路,把这个节点的下一个节点的值付给这个节点,然后把这个节点的next指针指向下一个节点的下一个节点,等于用下一个节点覆盖这个节点,这个思路就可以在O(1)时间复杂度内删除这个节点

typedef struct ListNode
{
	int data;
	struct ListNode *next;
}ListNode;

void DeleteNode(ListNode**head, ListNode *node)
{
	if(head == NULL || node == NULL)
		return;

	if(node->next != NULL)
	{
		ListNode *p = node->next;
		node->data = p->data;
		node->next = p->next;

		delete p;
		p = NULL;
	}
	else if(*head == *node)//这个节点是头节点
	{
		delete node;
		node = NULL;
		*head = NULL;
	}
	else//这个节点是最后一个节点
	{
		ListNode *p = *head;
		while(p->next != node)
			p = p->next;
		p->next = NULL;
		delete node;
		node = NULL;
	}
}
需要注意的是如果这个节点是头节点,或者是最后一个节点怎么办

6.输入一个数组,调整该数组的顺序,使得奇数位于数组的前面,偶数位于数组的后面

常规思维,从前往后遍历,找到一个偶数就把这个数后面的所有数往前移动一位,然后把这个数放到最后一个位置,时间复杂度为

O(n2),太高了解题思路,利用爽指针法,前后各一个指针,然后前指针找到的偶数和后指针找到的奇数交换就可以了,时间复

杂度为O(n)

void ReorderArray(int a[], int len)
{
    if(a == NULL || len <= 0)
        return ;
    int *p = a, *q = a+len-1;
    int temp;
    while(p < q)
    {   
        while(p < q && (*p & 0x1))
            p++;
        while(p < q && !(*q & 0x1))
            q--;
        if(p < q)
        {   
            temp = *p; 
            *p = *q; 
            *q = temp;
        }   
    }   
}

双指针法的应用常常能得到意外的收获

7.输入一个连表,求该链表的倒数第K个节点

采用双指针法,一个指向第一个节点,一个指向第K个节点,然后同时往后移动,两个差距为K,然后一个到达最后一个节点的时候,另一个就是倒数第K个节点

SLnode getKnode(SLnode head, int k)
{
    if(head == NULL || head->next == NULL || k == 0)
        return NULL;

    SLnode node1 = head;
    SLnode node2 = head->next;
    while(k >= 1 && node1 != NULL)
    {   
        node1 = node1->next;
        k--;
    }   
    if(node1 == NULL)
        return NULL;
    
    while(node1->next != NULL)
    {   
        node1 = node1->next;
        node2 = node2->next;
    }   
    return node2;
}


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

《剑指offer》系列---2 的相关文章

随机推荐

  • python统计三国高频词,画条形图,绘词云图

    文章目录 前言 思路 代码 效果 总结 前言 记录一次期末作业 要求 1 统计三国演义 下卷 前十的高频词 含出现次数 2 根据上题结果 绘制高频词出现次数的条形图 3 生成三国演义 下卷 词云图 思路 1 open打开读取整篇文档 2 使
  • Vetur can‘t find `package.json`

    Vetur can t find package json 重新装了一下vscode 重新装vetur插件的时候右下角老是弹出提示 并且vetur的格式化也用不了了 我的解决办法是重新安装了vetur的版本 扩展里面找到vetur插件 点击
  • powershell报错:“irm : 请求被中止: 未能创建 SSL/TLS 安全通道“

    问题描述 powershell 执行下载的时候 报错 irm 请求被中止 未能创建 SSL TLS 安全通道 此时系统 所有的网络下载 经过https安全加密方式的TLS请求都会报错 因为加密版本不匹配的问题 可以通过以下命令 查看当前加密
  • TypeScript Property ‘XXX‘ does not exist on type ‘never‘.

    开发过程中出现这个错误是因为Typescript在执行代码检查时在该对象没有定义相应属性 这个错误不致命 遇到该错误有以下几种解决办法 1 将对象设置成 any this targetArray this options find item
  • 面向对象之反射

    目录 反射 优点 实战案例 案例 使用内置函数改造 反射内建函数注意事项 实例方法绑定和非绑定的区别 动态增加属性方法的区别 反射 其实它的核心本质其实就是利用字符串的形式去对象 模块 中操作 查找 获取 删除 添加 成员 一种基于字符串的
  • 【Android -- 开源库】表格 SmartTable 的基本使用

    介绍 1 功能 快速配置自动生成表格 自动计算表格宽高 表格列标题组合 表格固定左序列 顶部序列 第一行 列标题 统计行 自动统计 排序 自定义统计规则 表格图文 序列号 列标题格式化 表格各组成背景 文字 网格 padding等配置 表格
  • C++中的RTTI

    文章目录 dynamic cast运算符 指针类型的dynamic cast 引用类型的dynamic cast typeid运算符 使用RTTI type info类 参考资料 RTTI Runtime Type Information
  • 计算机专业2021考研分数线,2021研究生国家分数线是多少

    2021年考研国家线公布 再看看外语国家线最高的是文学类a类地区53 b类地区50分 每年虽然英语线不高 但是很多学生还是折在英语上 可惜啊 2021考研国家线 国家线公布干什么 一 做出选择 考研本身就是一个选拔性的考试 有人考上 自然也
  • 《时代》评出100位AI领域最具影响力人物,黄仁勋、马斯克、萨姆·奥特曼在列...

    编辑 腾讯科技 郝博阳 郭晓静 翻译 金鹿 在过去的一个世纪里 时代 杂志的封面反映了塑造社会的力量 今年也是如此 生成式人工智能 Generative AI 无疑是今年最受关注的重塑社会的力量 我现在看到的创新水平比我一生中见过的要强几个
  • gRPC的C++编译及简单使用

    grpc的编译及简单使用 1 grpc相关参考文档 grpc 主页 https grpc io grpc 文档 https grpc io docs grpc 简介 https grpc io docs what is grpc intro
  • loadrunner入门教程(1)--概述

    文章目录 1 loadrunner介绍 2 特性 2 1 轻松创建虚拟用户 2 2 创建真实的负载 2 3 定位性能问题 3 工作原理 3 1 VuGen发生器 3 2 控制器 Controller 3 3 分析器 Analysis 4 工
  • 大数据开发:Hive DDL操作入门

    Hive针对于数据管理操作 提供了类SQL语言HQL 在Hadoop生态当中 Hive定位为数据仓库工具 对于数据的各种操作 也就是使用HQL来完成 而HQL查询 可以分为DDL和DML两个部分来掌握 今天的大数据开发学习分享 我们就先来讲
  • 【Java8】Guava——Preconditions

    Preconditions Precondition 是先决条件的意思 也叫前置条件 可以人为是使函数正常执行的参数需要满足的条件 Preconditions 这个静态工厂中 Guava 为我们提供了一系列的静态方法 用于帮助我们在函数执行
  • 50+ 可以帮助提高前端开发效率的 ChatGPT Prompts

    大厂技术 高级前端 Node进阶 点击上方 程序员成长指北 关注公众号 回复1 加入高级Node交流群 如果你已经厌倦了繁琐重复的编码日常 想要提升自己的效率 那你可是来对地方了 借助 ChatGPT 的强大能力 你可以简化你的工作流程 减
  • 数据结构与算法-列表(双向链表)设计及其排序算法

    0 概述 本文主要涵盖列表 双向链表 的设计及其排序算法的总结 列表是一种典型的动态存储结构 其中的数据 分散为一系列称作节点 node 的单位 节点之间通过指针相互索引和访问 为了引入新节点或删除原有节点 只需在局部调整少量相关节点之间的
  • VUE项目获取url中的参数

    获取url参数有两种情况 情况一 内部页面之间互相传值 这里先讲情况一 因为同一项目内互相传值比较简单 假如要从A页面跳转到B页面 并传值 就要在A页面这么写 this router push name B query Id this tI
  • webpack5配置解析

    webpack 配置文件 webpack config js entry output loader plugins mode webpack 命令即可打包 entry entry 入口起点 1 string gt src index js
  • 智能家居地址

    http blog yeelink net p 509
  • 神经网络在分类问题中的应用(反向传播算法)

    目录 1 W初始值的设定 2 反向传播算法 反向传播的实例 在其他的一些算法中对于分类问题易出现项数过多 过度拟合的情况 所以这里用一个新的方法来神经网络来解决问题 神经网络可以很好的适用特征空间n很大的情况 用图像来做一些名词解释 在图像
  • 《剑指offer》系列---2

    1 求斐波那契数列的第N项 这个题目很简单 讲递归的书上都是用这个来讲的 但是面试的时候 如果你写个递归 那估计会让人失望的 因为递归的效率真是一个问题 你可以测试一下 输入50 基本上得到结果的时间 够你去喝杯茶了 include