剑指offer读书笔记

一 基础知识

1 数组:
数组占据一块连续内存,按顺序存储元素。定义数组时,因为数组中数据连续,需要事先指定数组规模大小,根据大小分配内存。
由于数组内存连续,可以在O(1)时间内读/写任何元素,因此可以用数组来实现简单的hash表。为解决数组空间效率不高的问题,设计了多种动态数组,比如vector。
数组和指针的区别:

int getSize(int m[]){
    return sizeof(m);
    }
int data[] = {1,2,3,4,5};
int size1 = sizeof(data);//20,数组大小
int* data1 = data;
int size2 = sizeof(data1);//4,指针大小
int size3 = getsize(data);//4,指针大小,数组作为函数参数传递时,数组作为指针使用。

关于数组初始化:
1 指定数组大小的初始化:
数组类型数组名[数组大小] = {值列表};
int i[10]={1,2,3,4,5,6,7,8,9,10};
2 未指定大小的数组的初始化:
当需要在声明时初始化一个数组,我们还可以使用C++提供的自动计算数组大小的功能,只要不指明数组的大小即可。实际上,编译器通过计算初始化数据的个数来创建一个足够大的数组来保存这些初始的数据。例如:
int nums[]= {1, 2, 3, 4};
3 动态定义数组:
很多情况下,在预编译过程阶段,数组的长度是不能预先知道的,必须在程序运行时动态的给出。但是问题是,c++要求定义数组时,必须明确给定数组的大小,要不然编译通不过 。解决方案:用new开辟内存空间。如:
int Array[5];正确
int i=5;
int Array[i];错误,在编译阶段编译器并不知道 i 的值是多少
int size=50;
int *p=new int[size]; 是正确的
http://www.cnblogs.com/richardcpp/archive/2012/10/15/2724406.html
2 字符串:
常量字符串一般放在一个单独的内存区域,但当几个指针指向相同的常量字符串时,往往指向同一个内存地址。

char str1[] = "hello";
char str2[] = "hello";//1,2不同
char* str3 = "hello";
char* str4 = "hello";//3,4相同

3树的常考知识点:
DFS和BFS
二叉搜索树,堆,红黑树
4算法和数据操作
查找:数组有序,二分查找
排序:时间复杂度,快排(遍历非遍历写法)
5代码的完整性:功能测试,边界测试,负面测试
功能测试的时候,除了考虑普通的测试用例之外,一定要注意考虑是否溢出(大数问题),边界测试在递归和循环中非常重要,错误输入的处理(返回值,全局变量,抛出异常)

二 面试题汇总

  • 面试题3:二维数组中的查找
    数组特点:数组从左到右递增,从上到下递增

从右上角开始,如果当前数字大于目标,列减,如果当前数字小于目标,行加,相等,返回目标,否则查找失败。

  • 面试题4:替换空格
    替换字符串中的每个空格为”20%”

在原来的字符串上替换需要覆盖和修改字符串后面的内存,为了减少移动的次数,从后往前替换。
先遍历一遍字符串,统计出空格个数,计算出替换后字符串的长度(原字符串长度加上空格个数*2),设指针p1指向原字符串末尾,p2指向新字符串末尾:
如果p1指向空格:
string[p2–] = ‘0’;string[p2–] = ‘2’;string[p2–] = ‘%’;p1–;(倒序)
否则:string[p2–] = string[p1–];直接拷贝
终止条件是:p1>=0并且p2>=p1

  • 面试题5从尾到头打印链表

后进先出,栈实现;递归,每访问一个结点先递归输出结点后面的结点,然后输出该结点本身

  • 面试题6重建二叉树

根据前序遍历和中序遍历重建二叉树
利用前序遍历的第一个结点就是根节点的值,扫描中序遍历序列,确定根节点的值的位置,然后把前序序列和中序序列划分为左子树和右子树序列,递归来求解。

  • 面试题7用两个栈实现一个队列(画图)

进队列:把元素压如栈1
出队列:如果栈2不为空,直接弹出栈2栈顶元素;如果栈2为空,把栈1压入栈2,然后弹出栈2栈顶元素

  • 面试题8旋转数组中的最小数字
    将一个递增序列的数组做旋转,求最小元素

旋转数组等价于两个排序的子数组,而且前面的子数组大于后面的子数组。这种一定程度上有序的数组,可以考虑二分查找的思路。
当第一个元素大于等于最后一个元素的时候,如果中间值大于等于第一个元素,中间值位于前面的递增数组,第一个元素索引等于中间值索引;如果中间值小于等于最后一个元素,中间值位于后面的递增数组,最后一个元素的索引等于中间值的索引。
重点是:考虑不旋转的情况和重复元素的情况(顺序查找)

  • 面试题9斐波拉契数列

循环改递归,保留前两项的解法。注意问题变形

  • 面试题10二进制中1的个数

基本解法:移位,与1。
问题:负数右移左边的最高位补充的是1,这样移位最后会陷入死循环。修改方法是把1不断向左移动,判断32次。
最优解法:n&(n-1)把一个数最右边的一个1变为0,二进制中有多少个1,就进行多少次这样的操作
扩展:从m变到n,需要改变m二进制中的多少位?
思路:先求亦或,再求亦或中1的个数

面试题11 面试题12

  • 面试题13在O(1)的时间删除表结点

如果要删除的结点不是尾结点:把该结点的下一个结点的内容复制出来覆盖被删除的结点的内容,然后删除下一个结点。如果要删除的结点是尾结点,分为只有一个结点和多个结点的情况,其中多个结点只能顺序删除。

  • 面试题14调整数数组顺序使奇数位于偶数前面

如果数组中元素的相对位置可以改变,那么设立两个指针,一个指向头元素,一个指向尾元素,让头指针指向偶数,尾指针指向奇数,然后交换。
如果要求相对位置不变,可以采用冒泡排序,前者为偶数,后者为奇数的情况两两交换相邻元素。或者新建一个数组先把原数组中的奇数push进去再把偶数push进去,然后用新数组数据覆盖原数组即可。

  • 面试题15链表中的倒数第k个节点

重点:鲁棒性,考虑链表为空,k<=0,链表节点数少于k的情况
双指针来回

  • 面试题16反转链表

边界条件:头指针为空或者只有一个结点时
反转操作:三指针,不要出现断层

  • 面试题17合并两个有序链表

递归调用,注意判断两个链表头指针是否为空

  • 面试题18判断一棵树是不是另一棵树的子结构

首先在A中查找和B的根节点一样的结点,注意检查A或者B中为空的情况,然后判断A中以R为根节点的子树和B是否具有相同结构,通过递归来考虑。

  • 面试题19二叉树的镜像

前序遍历每个结点,如果遍历到的结点有子结点,交换它的左右子结点,最后得到树的镜像。

  • 面试题20顺时针打印矩阵

每一圈左上角的起点是start,从(0,0)开始,循环打印的条件是columns>startX*2并且rows>startY*2,注意四步打印的条件

  • 面试题22栈的压入弹出序列
    输入两个整数序列,第一个表示压入序列,第二个表示弹出序列,压入栈的数字都不相同,判断该序列是否为弹出序列。

如果下一个数字是栈顶元素,直接弹出,否则继续压入。如果到最后都没有找到下一个需要弹出的数字,返回false

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值