剑指offer中典型编程题小记


1、 求逆序对

转化为归并排序,递归求解,例如7564-〉 75 64-〉 57 46 –〉4 5 6 7,复杂度是O(nlogn)

插入排序,进行了多少次swap表示有多少个逆序对。O(n^2)

int CountReverseNum(int a[], int na, int b[], int nb)
{
int sum = 0;
int indexa = 0, indexb = 0;
int* temp = new int[na+nb];
int index = 0;
while(indexa < na && indexb < nb)
{
if (a[indexa] <= b[indexb])
temp[index++] = a[indexa++];
else
{
temp[index++] = b[indexb++];
sum += na - indexa;     //减少对比次数
}
}
memcpy(&temp[index], ((indexa == na)? b:a), (na+nb - index)*sizeof(int));
memcpy(a, temp, sizeof(int)*(na+nb));
return sum;
}
int CountReverseNum(int a[], int n)
{
if ( n == 1) return 0;


int sum = 0, len = n/2;
sum += CountReverseNum(a, len);
sum += CountReverseNum(a+len, len+(n&1));
sum += CountReverseNum(a, len, a+len, len+(n&1));
return sum;
}

 

2、 求链表的第一个公共点

一定会出现形状Y,在公共点处合并。

若为双向链表,问题容易解决,如果不是:

方法一、压栈,从链表尾查看不同点

方法二、先遍历一遍链表,得到每一链表的长度l1、l2。从长的一端开始遍历l1,遍历l1-l2+1个节点,然后两端同时开始遍历,直到相遇为止

 

变形:求两个叶节点的最低公共祖先

情况一、是二叉搜索树

从树根开始进行遍历,如果当前节点比两个值都小,则搜索它的右子树,如果比两个值都大,则搜索左子树,如果比期中一个小,另一个大,则该节点为所求节点

 

情况二、任意树,可转化为求链表的第一个公共点

 

3、 找出第1500个丑数(只能被2、3、5整除的数)

保存之前结果(排好序),下一个数为之前的结果*2、*3、*5的最小值,可以用三个指针分别指向*2、*3、*5的最后一个数的位置。

 

4、 数字在排序数组中出现的次数例如1 2 3 3 3 3 4 5 中3出现的次数

方法一、二分查找,如果中间为3,则向前面遍历,向后遍历,找到中间出现3的个数,最坏情况下是O(n)

方法二、用二分查找,找到首尾的3,然后end-first+1得出结论,O(logn)

 

5、 求二叉树深度

使用遍历方法,传递一个参数为树的高度,(nleft > nrigth)? nleft+1: nright+1

 

6、 判断平衡二叉树

条件,任何节点左右子树的深度不超过1

后序遍历,同时记录每个点的深度

 

7、 求数组中只出现一次的数,其它数字都出现两次

所有数进行异或,最后得到的数为所求数字

 

变形:如果有两个数出现1次,找到这两个数

先进行一次异或,根据异或结果中的某一位为1,将所有的数分成两组,分别作异或

for(i= 0; I < n; i++)

   resultExOr ^=data[i];

 

8、 从递增数组中找到和为s的两个数,例如 1 2 47 11 15 找到两个数,和为15

确定两个指针分别位于首尾,和若小,小的向前移动一位,和若大,大的向后移动一位

 

9、 翻转单词顺序  I am a student.   student. a am I

方法,翻转所有单词顺序,再翻转每一个单词顺序

        

         变形: 左旋若干位单词, 例如abcdef变成 cdefab

         ->先把ab提出,所有单词向前移动

         ->(a’b’) -> (a’b’)’-> ba

void ReverseWords(char* words);
int main()
{
char words[3][100];                 //注意,如果直接初始化为char* word=”hello“,内容是保存在常量数据区的,因此是不允许更改的!
strcpy(words[0] , "hello world!" );
strcpy(words[1] , "a" );
strcpy(words[2] , "l'm a student.");
for (int i = 0; i < 3; i++)
{
printf("%s", words[i]);
ReverseWords(words[i]);
printf(" -> %s\n", words[i]);
}
gets(words[0]);
return 0;
}


void Reverse(char* words, int start, int end)
{
if (end < start || start < 0)
return;
int i= start, j = end;
while ( i < j)
{
char temp = words[i];
words[i] = words[j];
words[j] = temp;
i++;
j--;
}
return;
}


void  ReverseWords(char* words)
{
if (words == NULL) return;
int size = 0;
while (words[size] != '\0') size++;
if (0 == size)
return ;


Reverse(words, 0, size -1);

int i = 0;
int start, end;
while(i < size)
{
start = i;
while (words[i] != ' ' && i < size) i++;
end = i -1;
Reverse(words, start, end);
i++;
}
return;
}

 

10、             n个骰子的点数之和,所有可能的值,及出现次数

方法一、递归

方法二、循环,每一次算加上第i个骰子后,可能的值

f(n) =f(n-1)+(fn-2)+f(n-3)+…+f(n-6)

只用保存当次循环值和上一次循环的值

 

11、             不用加减乘除做加法

a ^ b,得到是加过以后,未进行进位的值

(a & b) << 1,得到该进的那一位

do{

 sum= num1 ^ num2;

 carry= (num1 & num2) <<1;

 num1 = sum;

 num2 = carry;

}while(num2 !=  0)

 

12、             替换空格,实现一个函数,把字符串中每个空格替换成“%20”,例如 we arehappy替换成we%20are%20happy

         方法一:从头向尾检索,一旦发现空格便进行替换,并将之后的字符向后移位,复杂度O(n^2)

方法二:遍历一遍字符串,找到需要替换的总个数,计算最后字符串长度,然后从字符串尾开始向前替换,复杂度O(n)

 

13、             根据前序、中序遍历结果重建二叉树

14、             用两个栈实现队列

15、             找到旋转数组中的最小数字,例如 3 4 5 1 2 中最小的数字1

方法一:从头到尾进行遍历,复杂度O(n)

方法二:使用类似与二分查找的方式,用三个指针,第一个指向第一个元素,第二个指向最后一个元素,第三个指向中间元素,复杂度O(logn)

16、             非波那契数列,f(n) =f(n-1) + f(n-2)

方法一:递归计算,但是重复计算多

方法二:将之前的结果都保存起来,for循环计算时间复杂度O(n)

方法三:矩阵乘法计算,O(logn)

方法四:求解通项公式:f(n) = f(n-1)+f(n-2),的特征方程为:x^2 = x -1, 有根 x = (1+- sqrt(5))/2,所以存在A、B使得f(n) = a * ((1 - sqrt(5))/2) ^ n + B* ((1+sqrt(5))/2)^n

将f(0) = 0, f(1) = 1带入求解,解得A = -sqrt(5) /5 B = sqrt(5)/5

 

应用:

一只青蛙一次可以跳上一级台阶,也可以跳上2级,求该青蛙跳上一个n级台阶总共有多少种跳法。

f(n) = f(n-1) + f(n-2)

 

17、             求二进制中1的个数

方法一: 考虑到如果单纯进行右移并判断最后一位是否为1,对于负数可能出现死循环,因此,将原数与1、2、4...相与,但是复杂度为32次

   方法二:由于一个整数减去1再与它自己相与,刚好能使最后一个1变成0,由此循环多少次则可以计算有多少个1.

 

3.3 章

11、 数值的整数次方

         使用递归的方法,a^4 = a^2 *a^2,同时注意,0的次幂、负数的次幂等问题。可以使用全局标识来表示是否出错。

 

12、打印1到最大的n位数

         最重要是考虑到大数问题。使用字符串数组来表示。

        方法一:使用字符串数组模拟加法

         方法二:递归全排列

 

13、在O(1)的时间内删除链表节点

         方法一:从表头遍历到该节点,同时记录下前一个节点的位置,从而进行删除

         方法二:将该节点的下一个节点复制到该节点,然后删除下一个节点。!(包含假设,该节点一定在链表中!)

 

14、调整数组顺序使得奇数位于偶数前面

         方法一:参考快排,设定两个指针,分别指向首尾。

         方法二:考虑扩展性的方法,把判断的函数单写。

 

15、求链表中倒数第k个节点

         方法一:使用两个指针,第一个指针先指向第k个节点,第二个指针指向首节点,然后两个同时向后遍历

         注意:k的输入是否合法,链表是否为空

 

16、反转链表      

         方法一:从头到尾一个一个反转

         注意:只有一个节点的时候,如何处理

 

17、合并两个排序链表

         方法一: 两个指针,同时合并。

         注意特殊输入

 

18、             输入两颗树,判断A是不是B的子结构

前序遍历,递归操作

4章

19、二叉树镜像

         通过写实例分析,得出从头到尾交换左右子树即可。

 

20、顺时针打印矩阵

 

21、包含min函数的栈,栈中pop、push、min操作复杂度都为1

         使用一个辅助栈保存最小值,栈顶为最小值。压入3 辅助栈中为3,再压入4,辅助栈中为3 3

 

22、判断是否是栈的压入弹出序列。例如:45321是,但是43512就不是

         使用辅助栈,模拟入栈出栈过程,如果出现某一处无法找到出栈数,则不是。

 

23、从上往下打印二叉树

 

24、输入整数组,判断是否是二叉树的后序便历(5 7 6 9 11 10 8)

 

25、打印二叉树中节点值和为输入整数的所有路径

         采用递归的方法,记录下到某一节点的和,使用前序遍历。使用栈来保存路径。

 

26、复杂链表的复制,一个节点和能包含多个指向。

         先在原链表上复制一遍链表,再拆开两个链表。

 

27、二叉搜索树与双向链表

 

28、打印一个字符串所有可能的排序

         递归算法


 //打印字符串所有可能的排序
void GernerateString(char s[], int n, vector<char> &result, vector<int> &visit)
{
if (result.size() == n)
{
for(int i = 0; i < n; i++)
cout<<result[i];
cout<<endl;
return;
}
for (int i =0; i < n; i++)
{
if (!visit[i])
{
result.push_back(s[i]);
visit[i] = 1;
GernerateString(s, n, result, visit);
visit[i] = 0;
result.pop_back();
}
}
}
void PrintAllString(char s[], int n)
{
vector<char> result;
vector<int> visit( n, 0);
GernerateString(s, n, result, visit);
}

 

48、不能被继承的类

解法一:将构造函数设为私有函数,通过静态函数来调用他的构造函数。任何继承都会在编译时报错。但这样的定义,得到的变量只能位于堆上,而不能位于栈上。

class SealedClass1

{

public:

static SealedClass1 * GetInstance(){ return new Instance;}

static void DeleteInstance(){ delete pInstance;}

private:

SealedClass1() {}

~SealedClass1(){ }

};

解法二:利用虚拟继承。SealedClass2在使用上跟别的类型没有区别,当我们试图从SealedClass2中继承一个类并创建它的实例的时候,却不能通过。因为会调用MakeSealed的私有函数。这样的做法有一个问题,是它的移植性不好,GCC中不支持将模板参数类型设为友元类型。

template <typename T> 

class MakeSealed

{

friend T;

private:

MakeSealed(){}

~MakeSealed(){}

};


class SealedClass2 : virtual public MakeSealed<SealedClass2>

{

public:

SealedClass2(){}

~SealedClass2(){}
}

 



     

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值